初始化
commit
31e9432ba0
|
@ -0,0 +1,19 @@
|
|||
APP_DEBUG = true
|
||||
APP_TRACE = true
|
||||
|
||||
[APP]
|
||||
DEFAULT_TIMEZONE = Asia/Shanghai
|
||||
|
||||
[DATABASE]
|
||||
TYPE = mysql
|
||||
HOSTNAME = 211.149.245.223
|
||||
DATABASE = dev_bee_cms
|
||||
USERNAME = dev_bee_cms
|
||||
PASSWORD = dT7yH5fmd28JG6ER
|
||||
HOSTPORT = 3306
|
||||
CHARSET = utf8mb4
|
||||
DEBUG = true
|
||||
PREFIX = bee_
|
||||
|
||||
[LANG]
|
||||
default_lang = zh-cn
|
|
@ -0,0 +1,14 @@
|
|||
.env
|
||||
/.idea
|
||||
/.vscode
|
||||
backup/data/*
|
||||
/config/extra
|
||||
*.log
|
||||
runtime/*
|
||||
storage/*
|
||||
public/storage/*
|
||||
.DS_Store
|
||||
/Test.php
|
||||
nginx.htaccess
|
||||
dump.rdb
|
||||
/app/controller/api/Test.php
|
|
@ -0,0 +1,32 @@
|
|||
|
||||
ThinkPHP遵循Apache2开源协议发布,并提供免费使用。
|
||||
版权所有Copyright © 2006-2016 by ThinkPHP (http://thinkphp.cn)
|
||||
All rights reserved。
|
||||
ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。
|
||||
|
||||
Apache Licence是著名的非盈利开源组织Apache采用的协议。
|
||||
该协议和BSD类似,鼓励代码共享和尊重原作者的著作权,
|
||||
允许代码修改,再作为开源或商业软件发布。需要满足
|
||||
的条件:
|
||||
1. 需要给代码的用户一份Apache Licence ;
|
||||
2. 如果你修改了代码,需要在被修改的文件中说明;
|
||||
3. 在延伸的代码中(修改和有源代码衍生的代码中)需要
|
||||
带有原来代码中的协议,商标,专利声明和其他原来作者规
|
||||
定需要包含的说明;
|
||||
4. 如果再发布的产品中包含一个Notice文件,则在Notice文
|
||||
件中需要带有本协议内容。你可以在Notice中增加自己的
|
||||
许可,但不可以表现为对Apache Licence构成更改。
|
||||
具体的协议参考:http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,61 @@
|
|||
## 本CMS基于ThinkPHP 6.0开发
|
||||
|
||||
> - 运行环境要求 PHP7.4+
|
||||
> - MySql版本限制 5.7+
|
||||
|
||||
<hr /><br />
|
||||
|
||||
### 功能列表 ###
|
||||
|
||||
> - [x] 后台菜单管理
|
||||
> - [x] 后台权限管理
|
||||
> - [x] 管理员管理
|
||||
> - [x] 操作日志
|
||||
> - [x] 配置文件配置
|
||||
> - [x] 素材管理
|
||||
> - [x] 内容管理
|
||||
> - [x] 栏目管理
|
||||
> - [x] 模型管理
|
||||
> - [x] 配置管理
|
||||
|
||||
<hr /><br />
|
||||
|
||||
### 待办功能列表 ###
|
||||
|
||||
> - 迁移|重构老版CMS功能
|
||||
>> 碎片管理【block高度自定义配置】
|
||||
> - 系统
|
||||
> - 配置管理重构
|
||||
>> 需求 配置优先查询配置文件,文件不存在则根据数据库生成配置文件
|
||||
> - 内容管理重构
|
||||
>> 拆分内容表为基础表,根据模型建表创建额外表
|
||||
> - 模型管理重构
|
||||
> - 相关SEO配置
|
||||
> - 组图插件完善
|
||||
>> 可排序、可自定义关联字段 如:标题、url、alt、描述、等等关联可自由增减
|
||||
|
||||
|
||||
<hr /><br />
|
||||
|
||||
### PHP扩展依赖列表 ###
|
||||
|
||||
> - fileinfo
|
||||
> - exif 需要安装在mbsting扩展之后
|
||||
> - gd
|
||||
|
||||
<hr /><br />
|
||||
|
||||
### PHP组件列表 ###
|
||||
|
||||
> - [图片处理 Intervention Image](http://image.intervention.io/getting_started/introduction)
|
||||
> - [权限验证 think-authz](https://github.com/php-casbin/think-authz)
|
||||
|
||||
<hr /><br />
|
||||
|
||||
### 前端组件列表 ###
|
||||
|
||||
> - [xm-Select 下拉选择框](https://gitee.com/maplemei/xm-select)
|
||||
> - [富文本编辑器选择 wangEditorv3.1.1](https://github.com/wangfupeng1988/wangEditor)
|
||||
|
||||
<hr /><br />
|
||||
|
|
@ -0,0 +1 @@
|
|||
deny from all
|
|
@ -0,0 +1,666 @@
|
|||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 流年 <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
use think\Collection;
|
||||
use think\db\exception\DbException;
|
||||
use think\exception\ClassNotFoundException;
|
||||
use think\Model;
|
||||
use think\Paginator;
|
||||
|
||||
// 应用公共文件
|
||||
|
||||
if (!function_exists('widget')) {
|
||||
/**
|
||||
* 渲染输出Widget
|
||||
* @param string $name Widget名称
|
||||
* @param array $data 传入的参数
|
||||
* @return mixed
|
||||
* milo 2019-05-08 从TP5.1代码中拿来修改的
|
||||
*/
|
||||
function widget($name, $data = [])
|
||||
{
|
||||
return action($name, $data, 'widget');
|
||||
}
|
||||
}
|
||||
if (!function_exists('action')) {
|
||||
/**
|
||||
* 调用模块的操作方法 参数格式 [模块/控制器/]操作
|
||||
* @param string $url 调用地址
|
||||
* @param string|array $vars 调用参数 支持字符串和数组
|
||||
* @param string $layer 要调用的控制层名称
|
||||
* @param bool $appendSuffix 是否添加类名后缀
|
||||
* @return mixed
|
||||
* milo 2019-05-08 从TP5.1代码中拿来修改的
|
||||
*/
|
||||
function action($url, $vars = [], $layer = 'controller', $appendSuffix = false)
|
||||
{
|
||||
$info = pathinfo($url);
|
||||
$action = $info['basename'];
|
||||
$module = '.' != $info['dirname'] ? $info['dirname'] : request()->controller();
|
||||
$class = controller($module, $layer);
|
||||
if (is_scalar($vars)) {
|
||||
if (strpos($vars, '=')) {
|
||||
parse_str($vars, $vars);
|
||||
} else {
|
||||
$vars = [$vars];
|
||||
}
|
||||
}
|
||||
return app()->invokeMethod([$class, $action . config('route.action_suffix')], $vars);
|
||||
}
|
||||
}
|
||||
if (!function_exists('controller')) {
|
||||
/**
|
||||
* 实例化(分层)控制器 格式:[模块名/]控制器名
|
||||
* @access public
|
||||
* @param string $name 资源地址
|
||||
* @param string $layer 控制层名称
|
||||
* @param bool $appendSuffix 是否添加类名后缀
|
||||
* @param string $empty 空控制器名称
|
||||
* @return object
|
||||
* @throws ClassNotFoundException
|
||||
*
|
||||
* milo 2019-05-08 从TP5.1代码中拿来修改的
|
||||
*/
|
||||
function controller($name, $layer = 'controller', $empty = '')
|
||||
{
|
||||
$class = parseClass($name, $layer);
|
||||
if (class_exists($class)) {
|
||||
return app()->make($class);
|
||||
} elseif ($empty && class_exists($emptyClass = app()->parseClass($layer, $empty))) {
|
||||
return app()->make($emptyClass);
|
||||
}
|
||||
|
||||
throw new ClassNotFoundException('class not exists:' . $class, $class);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('parseClass')) {
|
||||
/**
|
||||
* 解析模块和类名
|
||||
* @access protected
|
||||
* @param string $name 资源地址
|
||||
* @param string $layer 验证层名称
|
||||
* @param bool $appendSuffix 是否添加类名后缀
|
||||
* @return array
|
||||
*
|
||||
* milo 2019-05-08 从TP5.1代码中拿来修改的
|
||||
*/
|
||||
function parseClass($name, $layer)
|
||||
{
|
||||
if (false !== strpos($name, '\\')) {
|
||||
$class = $name;
|
||||
} else {
|
||||
if (strpos($name, '/')) {
|
||||
$names = explode('/', $name, 2);
|
||||
$name = $names[1];
|
||||
}
|
||||
$class = app()->parseClass($layer, $name);
|
||||
}
|
||||
return $class;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!function_exists('randomStr')) {
|
||||
/**
|
||||
* 获取随机字符串
|
||||
* @param int $type 0:数字(默认);1:全部;2:小写字母;3:大写字母;4:字母;
|
||||
* @param int $len 字符串长度
|
||||
* @return string
|
||||
*/
|
||||
function randomStr($type = 0, $len = 5)
|
||||
{
|
||||
$strPol = "0123456789";
|
||||
if ($type == 1) {
|
||||
$strPol = "ABCDEFGHIJKLMOPQRSTUVWYZ0123456789abcdefghijklmopqrstuvwyz";
|
||||
} elseif ($type == 2) {
|
||||
$strPol = "abcdefghijklmopqrstuvwyz";
|
||||
} elseif ($type == 3) {
|
||||
$strPol = "ABCDEFGHIJKLMOPQRSTUVWYZ";
|
||||
} elseif ($type == 4) {
|
||||
$strPol = "ABCDEFGHIJKLMOPQRSTUVWYZabcdefghijklmopqrstuvwyz";
|
||||
}
|
||||
$max = strlen($strPol) - 1;
|
||||
$str = '';
|
||||
for ($i = 0; $i < $len; $i++) {
|
||||
$str .= $strPol[rand(0, $max)];
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('isMobile')) {
|
||||
//判断访问终端是否为移动端
|
||||
function isMobile()
|
||||
{
|
||||
// 如果有HTTP_X_WAP_PROFILE则一定是移动设备
|
||||
if (isset($_SERVER['HTTP_X_WAP_PROFILE'])) {
|
||||
return true;
|
||||
}
|
||||
// 如果via信息含有wap则一定是移动设备,部分服务商会屏蔽该信息
|
||||
if (isset($_SERVER['HTTP_VIA'])) {
|
||||
// 找不到为flase,否则为true
|
||||
return stristr($_SERVER['HTTP_VIA'], "wap") ? true : false;
|
||||
}
|
||||
// 脑残法,判断手机发送的客户端标志,兼容性有待提高。其中'MicroMessenger'是电脑微信
|
||||
if (isset($_SERVER['HTTP_USER_AGENT'])) {
|
||||
$clientkeywords = [
|
||||
'nokia', 'sony', 'ericsson', 'mot', 'samsung', 'htc', 'sgh', 'lg', 'sharp', 'sie-', 'philips',
|
||||
'panasonic', 'alcatel', 'lenovo', 'iphone', 'ipod', 'blackberry', 'meizu', 'android', 'netfront',
|
||||
'symbian', 'ucweb', 'windowsce', 'palm', 'operamini', 'operamobi', 'openwave', 'nexusone', 'cldc',
|
||||
'midp', 'wap', 'mobile', 'MicroMessenger'
|
||||
];
|
||||
// 从HTTP_USER_AGENT中查找手机浏览器的关键字
|
||||
if (preg_match("/(" . implode('|', $clientkeywords) . ")/i", strtolower($_SERVER['HTTP_USER_AGENT']))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// 协议法,因为有可能不准确,放到最后判断
|
||||
if (isset ($_SERVER['HTTP_ACCEPT'])) {
|
||||
// 如果只支持wml并且不支持html那一定是移动设备
|
||||
// 如果支持wml和html但是wml在html之前则是移动设备
|
||||
if ((strpos($_SERVER['HTTP_ACCEPT'], 'vnd.wap.wml') !== false) && (strpos($_SERVER['HTTP_ACCEPT'],
|
||||
'text/html') === false || (strpos($_SERVER['HTTP_ACCEPT'],
|
||||
'vnd.wap.wml') < strpos($_SERVER['HTTP_ACCEPT'], 'text/html')))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
//根据栏目获取路径
|
||||
if (!function_exists('getUri')) {
|
||||
function getUri($category)
|
||||
{
|
||||
if (!empty($category) && isset($category['id'])) {
|
||||
if (empty($category['route'])) {
|
||||
return '/page/' . $category['id'] . ".html";
|
||||
} else {
|
||||
return $category['route'];
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
//根据文件大小转换为文字
|
||||
if (!function_exists('sizeToStr')) {
|
||||
function sizeToStr($size)
|
||||
{
|
||||
if (!is_numeric($size) || $size <= 0) {
|
||||
return '';
|
||||
}
|
||||
$size = $size / 1024;
|
||||
if ($size < 1024) {
|
||||
return sprintf("%.2fK", $size);
|
||||
}
|
||||
$size = $size / 1024;
|
||||
if ($size < 1024) {
|
||||
return sprintf("%.2fM", $size);
|
||||
}
|
||||
$size = $size / 1024;
|
||||
return sprintf("%.2fG", $size);
|
||||
}
|
||||
}
|
||||
|
||||
//根据路径获取文件名
|
||||
if (!function_exists('getKeyByPath')) {
|
||||
function getKeyByPath($path)
|
||||
{
|
||||
return substr($path, strrpos($path, '/') + 1);
|
||||
}
|
||||
}
|
||||
|
||||
//富文本中提取图片路径
|
||||
if (!function_exists('getImageUrlFromText')) {
|
||||
function getImageUrlFromText($content)
|
||||
{
|
||||
preg_match_all('/<img.*?src="(.*?)".*?>/is', $content, $imgs);
|
||||
$data = [];
|
||||
if (!empty($imgs) && !empty($imgs[1])) {
|
||||
foreach ($imgs[1] as $img) {
|
||||
if (substr($img, 0, 4) != 'http') {
|
||||
$key = getKeyByPath($img);
|
||||
$data[$key] = $img;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
//富文本中提取视频路径
|
||||
if (!function_exists('getVideoUrlFromText')) {
|
||||
function getVideoUrlFromText($content)
|
||||
{
|
||||
preg_match_all('/<video.*?src="(.*?)".*?>/is', $content, $videos);
|
||||
$data = [];
|
||||
if (!empty($videos) && !empty($videos[1])) {
|
||||
foreach ($videos[1] as $video) {
|
||||
if (substr($video, 0, 4) != 'http') {
|
||||
$key = getKeyByPath($video);
|
||||
$data[$key] = $video;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
//获取目录下的所有文件
|
||||
if (!function_exists('getAllFilesByPath')) {
|
||||
function getAllFilesByPath($path, $rootPath)
|
||||
{
|
||||
if (is_dir($path) && file_exists($path)) {
|
||||
$items = scandir($path);
|
||||
$files = [];
|
||||
foreach ($items as $item) {
|
||||
if (substr($item, 0, 1) != '.' && strpos($item, '_') == false) {
|
||||
$itemPath = $path . '/' . $item;
|
||||
if (is_file($itemPath)) {
|
||||
$size = filesize($itemPath);
|
||||
$files[$item] = [
|
||||
'path' => str_replace($rootPath, '/', $itemPath),
|
||||
'realPath' => $itemPath,
|
||||
'size' => $size,
|
||||
'sizeStr' => sizeToStr($size),
|
||||
'suffix' => strtolower(substr($item, strrpos($item, '.') + 1)) //后缀
|
||||
];
|
||||
} elseif (is_dir($itemPath)) {
|
||||
$childFiles = getAllFilesByPath($itemPath, $rootPath);
|
||||
if (!empty($childFiles)) {
|
||||
$files = array_merge($files, $childFiles);
|
||||
} else {
|
||||
rmdir($itemPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $files;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
//过滤get输入
|
||||
if (!function_exists('getFilter')) {
|
||||
function getFilter($value)
|
||||
{
|
||||
$getFilter = "'|(and|or)\b.+?(>|<|=|in|like)|\/\*.+?\*\/|<\s*script\b|\bEXEC\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\s+(TABLE|DATABASE)";
|
||||
$forArray = false;
|
||||
if (is_array($value)) {
|
||||
$forFilter = implode($value);
|
||||
$forArray = true;
|
||||
} else {
|
||||
$forFilter = $value;
|
||||
}
|
||||
if (preg_match("/" . $getFilter . "/is", $forFilter) == 1) {
|
||||
$value = $forArray ? [] : '';
|
||||
}
|
||||
return filterExp($value);
|
||||
}
|
||||
}
|
||||
|
||||
//过滤post录入
|
||||
if (!function_exists('postFilter')) {
|
||||
function postFilter($value)
|
||||
{
|
||||
$postFilter = "\b(and|or)\b.{1,6}?(=|>|<|\bin\b|\blike\b)|\/\*.+?\*\/|<\s*script\b|\bEXEC\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\s+(TABLE|DATABASE)";
|
||||
$forArray = false;
|
||||
if (is_array($value)) {
|
||||
$forFilter = implode($value);
|
||||
$forArray = true;
|
||||
} else {
|
||||
$forFilter = $value;
|
||||
}
|
||||
if (preg_match("/" . $postFilter . "/is", $forFilter) == 1) {
|
||||
$value = $forArray ? [] : '';
|
||||
}
|
||||
return filterExp($value);
|
||||
}
|
||||
}
|
||||
|
||||
//过滤cookie数据
|
||||
if (!function_exists('cookieFilter')) {
|
||||
function cookieFilter($value)
|
||||
{
|
||||
$cookieFilter = "\b(and|or)\b.{1,6}?(=|>|<|\bin\b|\blike\b)|\/\*.+?\*\/|<\s*script\b|\bEXEC\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\s+(TABLE|DATABASE)";
|
||||
$forArray = false;
|
||||
if (is_array($value)) {
|
||||
$forFilter = implode($value);
|
||||
$forArray = true;
|
||||
} else {
|
||||
$forFilter = $value;
|
||||
}
|
||||
if (preg_match("/" . $cookieFilter . "/is", $forFilter) == 1) {
|
||||
$value = $forArray ? [] : '';
|
||||
}
|
||||
return filterExp($value);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('filterExp')) {
|
||||
function filterExp($value)
|
||||
{
|
||||
$filter = '/^(\s)+(EXP|NEQ|GT|EGT|LT|ELT|OR|XOR|LIKE|NOTLIKE|NOT LIKE|NOT BETWEEN|NOTBETWEEN|BETWEEN|NOTIN|NOT IN|IN)(\s)+$/i';
|
||||
$forArray = false;
|
||||
if (is_array($value)) {
|
||||
$forFilter = implode($value);
|
||||
$forArray = true;
|
||||
} else {
|
||||
$forFilter = $value;
|
||||
}
|
||||
if (preg_match($filter, $forFilter) == 1) {
|
||||
$value = $forArray ? [] : '';
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('arrayHtmlFilter')) {
|
||||
function arrayHtmlFilter($arr)
|
||||
{
|
||||
foreach ($arr as $k => $one) {
|
||||
if (is_array($one)) {
|
||||
$arr[$k] = arrayHtmlFilter($one);
|
||||
} else {
|
||||
$one = trim($one);
|
||||
$arr[$k] = strip_tags($one);
|
||||
}
|
||||
}
|
||||
return $arr;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('toCamelString')) {
|
||||
/**
|
||||
* 转驼峰
|
||||
*
|
||||
* @param string $string
|
||||
* @param false $small 默认false 为true首字母小写
|
||||
* @return string
|
||||
*/
|
||||
function toCamelString(string $string, bool $small = false): string
|
||||
{
|
||||
//例: xxx_yYy_zzZ 和 xxX-yyy-Zzz
|
||||
//1. 字符串转小写 xxx_yyy_zzz xxx-yyy-zzz
|
||||
//2. -和_转空格 xxx yyy zzz
|
||||
//3. 单词首字母大写 Xxx Yyy Zzz
|
||||
//4. 清除空格 XxxYyyZzz
|
||||
//处理下划线、减号 统统专程大驼峰 如xxx_yyy_zzz xxx-yyy-zzz 均转为 XxxYyyZzz
|
||||
$string = strtolower($string);
|
||||
$string = str_replace('-', ' ', $string);
|
||||
$string = str_replace('_', ' ', $string);
|
||||
$string = str_replace(' ', '', ucwords($string));
|
||||
if ($small) {
|
||||
$string = lcfirst($string);
|
||||
}
|
||||
return $string;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('unCamelize')) {
|
||||
/**
|
||||
* 驼峰命名转特定分隔符[如下划线]命名
|
||||
* @param string $camelCaps
|
||||
* @param string $separator
|
||||
* @return string
|
||||
*/
|
||||
function unCamelize(string $camelCaps, string $separator = '-'): string
|
||||
{
|
||||
return strtolower(preg_replace('/([a-z])([A-Z])/', "$1" . $separator . "$2", $camelCaps));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!function_exists('generateRand')) {
|
||||
/**
|
||||
* 生成随机数
|
||||
*
|
||||
* @param int $length 长度
|
||||
* @param string $type 模式 默认mix混合 number纯数字 alpha字母
|
||||
* @return string
|
||||
*/
|
||||
function generateRand(int $length = 8, string $type = 'mix'): string
|
||||
{
|
||||
$alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
$number = '0123456789';
|
||||
$alphabetLen = strlen($alphabet) - 1;
|
||||
$numberLen = 9;
|
||||
|
||||
switch ($type) {
|
||||
case 'number':
|
||||
$str = $number;
|
||||
$len = $numberLen;
|
||||
break;
|
||||
case 'alpha':
|
||||
$str = $alphabet;
|
||||
$len = $alphabetLen;
|
||||
break;
|
||||
default:
|
||||
$str = $alphabet . $number;
|
||||
$len = $alphabetLen + $numberLen;
|
||||
}
|
||||
|
||||
$randStr = '';
|
||||
$str = str_shuffle($str);
|
||||
for ($i = 0; $i < $length; $i++) {
|
||||
$num = mt_rand(0, $len);
|
||||
$randStr .= $str[$num];
|
||||
}
|
||||
return $randStr;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('generateCode')) {
|
||||
/**
|
||||
* 生成特定编码
|
||||
*
|
||||
* @param string $type 类型 默认mix混合 number纯数字 alpha字母
|
||||
* @param int $len 随机数长度
|
||||
* @return string
|
||||
*/
|
||||
function generateCode(string $type = 'number', int $len = 6): string
|
||||
{
|
||||
//#时间戳+微秒+6位随机数
|
||||
$time = microtime(true);
|
||||
$timeStr = str_replace('.', '', $time);
|
||||
return sprintf("%s%s", $timeStr, generateRand($len, $type));
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('checkMobile')) {
|
||||
/**
|
||||
* 检测手机号
|
||||
*
|
||||
* @param string $mobile
|
||||
* @return bool
|
||||
*/
|
||||
function checkMobile(string $mobile): bool
|
||||
{
|
||||
if (preg_match("/^1[3456789]{1}\d{9}$/", $mobile)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('arrayKeysFilter')) {
|
||||
/**
|
||||
* 数组键名过滤
|
||||
*
|
||||
* @param array $data
|
||||
* @param array $allowKeys
|
||||
* @return array
|
||||
*/
|
||||
function arrayKeysFilter(array $data, array $allowKeys): array
|
||||
{
|
||||
$list = [];
|
||||
foreach ($data as $key => $val) {
|
||||
if (in_array($key, $allowKeys)) {
|
||||
$list[$key] = $val;
|
||||
}
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('getFilesize')) {
|
||||
/**
|
||||
* 尺寸单位转换
|
||||
*
|
||||
* @param $num
|
||||
* @return string
|
||||
*/
|
||||
function getFilesize($num): string
|
||||
{
|
||||
$p = 0;
|
||||
$format = 'B';
|
||||
if ($num > 0 && $num < 1024) {
|
||||
return number_format($num) . ' ' . $format;
|
||||
}
|
||||
if ($num >= 1024 && $num < pow(1024, 2)) {
|
||||
$p = 1;
|
||||
$format = 'KB';
|
||||
}
|
||||
if ($num >= pow(1024, 2) && $num < pow(1024, 3)) {
|
||||
$p = 2;
|
||||
$format = 'MB';
|
||||
}
|
||||
if ($num >= pow(1024, 3) && $num < pow(1024, 4)) {
|
||||
$p = 3;
|
||||
$format = 'GB';
|
||||
}
|
||||
if ($num >= pow(1024, 4) && $num < pow(1024, 5)) {
|
||||
$p = 3;
|
||||
$format = 'TB';
|
||||
}
|
||||
$num /= pow(1024, $p);
|
||||
return number_format($num, 3) . ' ' . $format;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('arrayNullToString')) {
|
||||
/**
|
||||
* 数组|或数据集中null值转为空字符串,并以数组格式返回
|
||||
* 通常用于api json 返回内容null转换
|
||||
*
|
||||
* @param array $data 【array|collection】
|
||||
* @return array
|
||||
*/
|
||||
function arrayNullToString($data)
|
||||
{
|
||||
if ($data instanceof Collection || $data instanceof Model) {
|
||||
$data = $data->toArray();
|
||||
}
|
||||
// 判断是否可以遍历
|
||||
if (is_iterable($data)) {
|
||||
foreach ($data as $key => $val) {
|
||||
if ($val instanceof Collection || $data instanceof Model) {
|
||||
$val = $val->toArray();
|
||||
}
|
||||
if (is_iterable($val)) {
|
||||
$data[$key] = arrayNullToString($val);
|
||||
} elseif ($val === null) {
|
||||
$data[$key] = '';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$data = [];
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('arrayKeysExcludeFilter')) {
|
||||
/**
|
||||
* 数组键名排除过滤
|
||||
*
|
||||
* @param array $data
|
||||
* @param array $excludeKeys 排除的字段
|
||||
* @return array
|
||||
*/
|
||||
function arrayKeysExcludeFilter(array $data, array $excludeKeys): array
|
||||
{
|
||||
foreach ($data as $key => $val) {
|
||||
if (in_array($key, $excludeKeys)) {
|
||||
unset($data[$key]);
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('getLatelyWeekDate')) {
|
||||
/**
|
||||
* 获取本周的周一 到周日的日期
|
||||
*/
|
||||
function getLatelyWeekDate()
|
||||
{
|
||||
//本周一
|
||||
$oneDate = date('Y-m-d 00:00:00', (time() - ((date('w') == 0 ? 7 : date('w')) - 1) * 86400)); //w为星期几的数字形式,这里0为周日
|
||||
$oneDateTime = strtotime($oneDate);
|
||||
//返回周一到周天 1-7
|
||||
return [
|
||||
"1" => ["date"=>$oneDate],
|
||||
"2" => ["date"=>date('Y-m-d 00:00:00', $oneDateTime + ((86400 * 1)))],
|
||||
"3" => ["date"=>date('Y-m-d 00:00:00', $oneDateTime + ((86400 * 2)))],
|
||||
"4" => ["date"=>date('Y-m-d 00:00:00', $oneDateTime + ((86400 * 3)))],
|
||||
"5" => ["date"=>date('Y-m-d 00:00:00', $oneDateTime + ((86400 * 4)))],
|
||||
"6" => ["date"=>date('Y-m-d 00:00:00', $oneDateTime + ((86400 * 5)))],
|
||||
"7" => ["date"=>date('Y-m-d 00:00:00', $oneDateTime + ((86400 * 6)))],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('stringDesensitization')) {
|
||||
/**
|
||||
* 字符串脱敏 默认给手机号脱敏 其他未兼容
|
||||
*
|
||||
* @param string $string
|
||||
* @param string $s
|
||||
* @param int $start
|
||||
* @param int $len
|
||||
* @return string
|
||||
*/
|
||||
function stringDesensitization(string $string, string $s = '****', int $start = 3, int $len = 4): string
|
||||
{
|
||||
return substr_replace($string, $s, $start, $len);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('checkPathExistWithMake')) {
|
||||
/**
|
||||
* 检测文件夹是否存在,不存在时自动创建(需要有写入权限)
|
||||
* 支持递归创建
|
||||
*
|
||||
* @param string $absolutePath
|
||||
* @return bool
|
||||
*/
|
||||
function checkPathExistWithMake(string $absolutePath): bool
|
||||
{
|
||||
try {
|
||||
$absolutePath = rtrim($absolutePath, '/');
|
||||
if (empty($absolutePath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_dir($absolutePath)) {
|
||||
return mkdir($absolutePath, 0777, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
namespace app\controller;
|
||||
|
||||
use app\model\ArchivesCategory as ArchivesCategoryModel;
|
||||
use app\model\Category;
|
||||
use app\model\Link;
|
||||
use app\model\System;
|
||||
use app\repository\BlockRepository;
|
||||
use app\repository\CmsRepository;
|
||||
use think\facade\Cache;
|
||||
use think\facade\Config;
|
||||
|
||||
/**
|
||||
* 控制器基础类
|
||||
*/
|
||||
class Base extends BaseController
|
||||
{
|
||||
//需要向模板传递的值
|
||||
protected $data = [
|
||||
"activeCategoryId"=> 0,//当前选中的顶级栏目
|
||||
"links"=> [],//友情连接
|
||||
"article"=> [],//文章
|
||||
"blocks"=> [],//碎片
|
||||
];
|
||||
//系统配置信息
|
||||
protected $system = [];
|
||||
|
||||
protected $auth = [];
|
||||
|
||||
protected $authId = 0;
|
||||
|
||||
protected $aboutCategory = [];
|
||||
|
||||
// 初始化
|
||||
protected function initialize()
|
||||
{
|
||||
$this->auth = session('frontend_auth') ?? [];
|
||||
$this->data['auth'] = $this->auth;
|
||||
$this->authId = $this->auth['id'] ?? 0;
|
||||
//加载基础配置
|
||||
Config::load('extra/base', 'extra_system');
|
||||
$this->system = config("extra_system");
|
||||
$this->data['system'] = $this->system;
|
||||
$this->setSeo();
|
||||
$this->setLinks();
|
||||
$this->setActiveCategory(ArchivesCategoryModel::index_id);
|
||||
}
|
||||
|
||||
//设置选中的栏目
|
||||
protected function setActiveCategory($id)
|
||||
{
|
||||
$this->data["active_category_id"] = $id;
|
||||
}
|
||||
|
||||
//设置友情链接 缓存1小时
|
||||
protected function setLinks()
|
||||
{
|
||||
if(Cache::has("links")){
|
||||
$this->data["links"] = Cache::get("links",[]);
|
||||
}else{
|
||||
$links = Link::findList([],[],1,20,null,["sort"=>"asc"]);
|
||||
if(isset( $links['list'])){
|
||||
$this->data["links"] = $links['list']->toArray();
|
||||
}else{
|
||||
$this->data["links"] = [];
|
||||
}
|
||||
Cache::set("links",$this->data["links"],3600);
|
||||
}
|
||||
}
|
||||
|
||||
//设置SEO信息
|
||||
protected function setSeo($title = '', $keywords = '', $description = '')
|
||||
{
|
||||
$this->data['seoTitle'] = $title ?: $this->system['seo_title'] ?? '';
|
||||
$this->data['seoKeywords'] = $keywords ?: $this->system['seo_keywords'] ?? '';
|
||||
$this->data['seoDescription'] = $description ?: $this->system['seo_description'] ?? '';
|
||||
}
|
||||
|
||||
//模板
|
||||
protected function view($template = '')
|
||||
{
|
||||
return view($template)->assign($this->data);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,254 @@
|
|||
<?php
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\controller;
|
||||
|
||||
use think\{App, Request, response\Json, response\Redirect, response\View, Validate};
|
||||
use Exception;
|
||||
use think\exception\ValidateException;
|
||||
|
||||
/**
|
||||
* 控制器基础类
|
||||
*/
|
||||
abstract class BaseController
|
||||
{
|
||||
/**
|
||||
* 无需登录的方法,同时也就不需要鉴权了
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $noNeedLogin = [];
|
||||
|
||||
/**
|
||||
* 无需鉴权的方法,但需要登录
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $noNeedRight = [];
|
||||
|
||||
/**
|
||||
* Request实例
|
||||
* @var Request
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* 应用实例
|
||||
* @var App
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* 是否批量验证
|
||||
* @var bool
|
||||
*/
|
||||
protected $batchValidate = false;
|
||||
|
||||
/**
|
||||
* 控制器中间件
|
||||
* @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()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证数据
|
||||
* @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, $validate, array $message = [], bool $batch = false)
|
||||
{
|
||||
if (is_array($validate)) {
|
||||
$v = new Validate();
|
||||
$v->rule($validate);
|
||||
} else {
|
||||
if (strpos($validate, '.')) {
|
||||
// 支持场景
|
||||
list($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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证器
|
||||
*
|
||||
* @param array $data
|
||||
* @param $validate
|
||||
* @param array $message
|
||||
* @param bool $batch
|
||||
* @return Json|bool
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function validateByApi(array $data, $validate, array $message = [], bool $batch = false)
|
||||
{
|
||||
try {
|
||||
$this->validate($data, $validate, $message, $batch);
|
||||
return true;
|
||||
} catch (ValidateException $e) {
|
||||
$msg = $e->getMessage();
|
||||
if ($batch) {
|
||||
$msg = implode(',', $e->getError());
|
||||
}
|
||||
|
||||
return $this->json(4000, $msg);
|
||||
} catch (Exception $e) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证器
|
||||
*
|
||||
* @param array $data
|
||||
* @param $validate
|
||||
* @param array $message
|
||||
* @param bool $batch
|
||||
* @return Redirect
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function validateByView(array $data, $validate, array $message = [], bool $batch = false): Redirect
|
||||
{
|
||||
try {
|
||||
$this->validate($data, $validate, $message, $batch);
|
||||
} catch (ValidateException $e) {
|
||||
$msg = $e->getMessage();
|
||||
if ($batch) {
|
||||
$msg = implode(',', $e->getError());
|
||||
}
|
||||
|
||||
return $this->error( $msg);
|
||||
} catch (Exception $e) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作成功跳转的快捷方法
|
||||
* @access protected
|
||||
* @param mixed $msg 提示信息
|
||||
* @param string|null $url 跳转的URL地址
|
||||
* @param mixed $data 返回的数据
|
||||
* @param integer $wait 跳转等待时间
|
||||
* @param array $header 发送的Header信息
|
||||
* @return Redirect
|
||||
*/
|
||||
protected function success($msg = '', string $url = null, $data = '', int $wait = 3, array $header = []): Redirect
|
||||
{
|
||||
if (is_null($url) && isset($_SERVER["HTTP_REFERER"])) {
|
||||
$url = $_SERVER["HTTP_REFERER"];
|
||||
} elseif ($url) {
|
||||
$url = (strpos($url, '://') || 0 === strpos($url, '/')) ? $url : $this->app->route->buildUrl($url);
|
||||
}
|
||||
|
||||
$result = [
|
||||
'code' => 1,
|
||||
'msg' => $msg,
|
||||
'data' => $data,
|
||||
'url' => $url,
|
||||
'wait' => $wait,
|
||||
];
|
||||
return $this->redirect(url('error/404', $result));
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作错误跳转的快捷方法
|
||||
* @access protected
|
||||
* @param mixed $msg 提示信息
|
||||
* @param string|null $url 跳转的URL地址
|
||||
* @param mixed $data 返回的数据
|
||||
* @param integer $wait 跳转等待时间
|
||||
* @return Redirect
|
||||
*/
|
||||
protected function error($msg = '', string $url = null, $data = '', int $wait = 3): Redirect
|
||||
{
|
||||
if (is_null($url)) {
|
||||
$referer = $_SERVER['HTTP_REFERER'] ?? null;
|
||||
if (empty($referer)) {
|
||||
$url = $this->request->isAjax() ? '' : '/';
|
||||
} else {
|
||||
$url = $this->request->isAjax() ? '' : 'javascript:history.back(-1);';
|
||||
}
|
||||
} elseif ($url) {
|
||||
$url = (strpos($url, '://') || 0 === strpos($url, '/')) ? $url : $this->app->route->buildUrl($url);
|
||||
}
|
||||
$result = [
|
||||
'code' => 0,
|
||||
'msg' => $msg,
|
||||
'data' => $data,
|
||||
'url' => $url,
|
||||
'wait' => $wait,
|
||||
];
|
||||
|
||||
return $this->redirect(url('error/404', $result));
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回封装后的API数据到客户端
|
||||
* 以json格式抛出异常
|
||||
* @access protected
|
||||
* @param integer $code 返回的code
|
||||
* @param mixed $msg 提示信息
|
||||
* @param mixed $data 要返回的数据
|
||||
* @return Json
|
||||
*/
|
||||
protected function json(int $code = 0, $msg = '操作成功', $data = []): Json
|
||||
{
|
||||
$result = [
|
||||
'code' => $code,
|
||||
'msg' => $msg,
|
||||
'data' => $data
|
||||
];
|
||||
return json($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* URL重定向
|
||||
* @access protected
|
||||
* @param string $url 跳转的URL表达式
|
||||
* @return Redirect
|
||||
*/
|
||||
protected function redirect($url): Redirect
|
||||
{
|
||||
if (!is_string($url)) {
|
||||
$url = $url->__toString();
|
||||
}
|
||||
return redirect($url);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace app\controller;
|
||||
|
||||
|
||||
class Error extends BaseController
|
||||
{
|
||||
public function __call($method, $args)
|
||||
{
|
||||
if(request()->isAjax()) {
|
||||
return $this->json(4004, 'error request!');
|
||||
} else {
|
||||
$referer = $_SERVER['HTTP_REFERER'] ?? null;
|
||||
if (empty($referer)) {
|
||||
$url = '/';
|
||||
} else {
|
||||
$domain = $this->request->domain();
|
||||
$urlInfo = parse_url($referer);
|
||||
$scheme = $urlInfo['scheme'] ?? '';
|
||||
$requestSrc = '';
|
||||
if (!empty($scheme)) {
|
||||
$requestSrc = $scheme.'://'.($urlInfo['host'] ?? '');
|
||||
}
|
||||
if($domain != $requestSrc) {
|
||||
$url = '/';
|
||||
} else {
|
||||
$url = 'javascript:history.back(-1);';
|
||||
}
|
||||
}
|
||||
$result = [
|
||||
'code' => 404,
|
||||
'msg' => '无效请求! 没有找到相关资源',
|
||||
'data' => [],
|
||||
'url' => $url,
|
||||
'wait' => 5,
|
||||
];
|
||||
return view('/manager/error/jump')->assign($result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function jump()
|
||||
{
|
||||
$param = request()->param();
|
||||
return view()->assign($param);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
namespace app\controller;
|
||||
|
||||
|
||||
use app\model\Feedback as FeedbackModel;
|
||||
use app\repository\AccountRepository;
|
||||
use think\captcha\facade\Captcha;
|
||||
|
||||
/**
|
||||
* 留言与意见反馈
|
||||
*
|
||||
* Class Feedback
|
||||
* @package app\controller
|
||||
*/
|
||||
class Feedback extends Base
|
||||
{
|
||||
/**
|
||||
* 用户提交反馈信息
|
||||
*/
|
||||
public function submitFeedback()
|
||||
{
|
||||
if (!$this->request->isPost()) {
|
||||
return $this->json(4001, '请求方式错误!');
|
||||
}
|
||||
|
||||
$params = [
|
||||
'user_name' => $this->request->post('user_name/s', ''),
|
||||
'user_tel' => $this->request->post('user_tel/s', ''),
|
||||
'user_email' => $this->request->post('user_email/s', ''),
|
||||
'content' => $this->request->post('content/s', ''),
|
||||
'code' => $this->request->post('code/s', ''),
|
||||
];
|
||||
|
||||
try{
|
||||
$validate = $this->validateByApi($params, [
|
||||
'code|验证码'=>'require|captcha',
|
||||
'user_name|姓名' => 'chs|min:2|max:30',
|
||||
'user_tel|联系电话' => 'min:5|max:20|mobile',
|
||||
'user_email|邮箱地址' => 'email',
|
||||
'content|留言内容' => 'require|min:6|max:500',
|
||||
]);
|
||||
if ($validate !== true) {
|
||||
return $validate;
|
||||
}
|
||||
|
||||
if ($this->authId > 0) {
|
||||
$account = AccountRepository::getInstance()->findById($this->authId);
|
||||
if ($account) {
|
||||
$params['user_name'] = $params['user_name'] ?: ($account->nickname ?? '');
|
||||
$params['user_tel'] = $params['user_tel'] ?: ($account->mobile ?? '');
|
||||
}
|
||||
}
|
||||
|
||||
FeedbackModel::create([
|
||||
'account_id' => $this->authId,
|
||||
'user_name' => $params['user_name'] ?: '',
|
||||
'user_tel' => $params['user_tel'] ?: '',
|
||||
'user_email' => $params['user_email'],
|
||||
'content' => $params['content'],
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
]);
|
||||
|
||||
|
||||
return $this->json(0, '感谢你的留言!');
|
||||
} catch (\Exception $e) {
|
||||
return $this->json(5001, '服务器异常,留言信息提交失败!');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function code()
|
||||
{
|
||||
return Captcha::create('verify');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
namespace app\controller;
|
||||
use app\exception\RepositoryException;
|
||||
use app\model\Archives;
|
||||
use app\model\ArchivesCategory;
|
||||
use app\model\Block;
|
||||
use app\model\Slide;
|
||||
use app\model\SlidePosition;
|
||||
use Exception;
|
||||
use think\response\Redirect;
|
||||
|
||||
/**
|
||||
* auth 王兴龙 2022-02-18
|
||||
* */
|
||||
class Index extends Base
|
||||
{
|
||||
/**
|
||||
* @return Redirect
|
||||
* @throws Exception
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
|
||||
try {
|
||||
//banner
|
||||
$this->data['slide'] = Slide::findList([["position","=",SlidePosition::home_position]])['list'];
|
||||
$this->data['topCategoryId'] = ArchivesCategory::index_id ;
|
||||
|
||||
//碎片
|
||||
$this->data['blocks'] = Block:: getByCategoryId(ArchivesCategory::index_id);
|
||||
|
||||
return $this->view();
|
||||
}catch (RepositoryException $e){
|
||||
return $this->error("服务器错误");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,150 @@
|
|||
<?php
|
||||
namespace app\controller;
|
||||
use app\model\Archives;
|
||||
use app\model\ArchivesCategory as ArchivesCategoryModel;
|
||||
use app\model\ArchivesModel;
|
||||
use app\model\Block;
|
||||
use app\service\DxtcPage;
|
||||
use Exception;
|
||||
use think\Paginator;
|
||||
use think\response\Redirect;
|
||||
use think\response\View;
|
||||
/**
|
||||
* auth 王兴龙 2022-02-18
|
||||
* */
|
||||
class Page extends Base
|
||||
{
|
||||
/**
|
||||
* @return Redirect
|
||||
* @throws Exception
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$categoryId = input("categoryId/d",0);
|
||||
|
||||
|
||||
if($categoryId==ArchivesCategoryModel::index_id){
|
||||
return $this->redirect("/");
|
||||
}
|
||||
$category = ArchivesCategoryModel::findById($categoryId);
|
||||
if(empty($category)){
|
||||
return $this->error("内容不存在");
|
||||
}
|
||||
|
||||
|
||||
//如果有链接
|
||||
if(!empty($category['link'])){
|
||||
return $this->redirect($category['link']);
|
||||
}
|
||||
|
||||
$this->data["category"] = $category;
|
||||
$this->setActiveCategory($category['id']);
|
||||
$this->data['topCategoryId'] = ArchivesCategoryModel::firstGradeById($category['id']) ;
|
||||
|
||||
$categoryModel = ArchivesModel::allModel();
|
||||
//所有类型都要把碎片加上
|
||||
$this->data["blocks"] = Block:: getByCategoryId($category['id']);
|
||||
|
||||
switch ($category['model_id']){
|
||||
//文章模型
|
||||
case $categoryModel[ArchivesModel::MODEL_ARCHIVES]:
|
||||
return $this->archives($category,$category['cover_template']);
|
||||
break;
|
||||
default:
|
||||
return $this->redirect("/");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//文章列表
|
||||
|
||||
/**
|
||||
*
|
||||
* @param $categoryId 栏目id
|
||||
* @param $categoryTemplate 模板
|
||||
*/
|
||||
protected function archives($category,$categoryTemplate)
|
||||
{
|
||||
//动态设置当前分页驱动
|
||||
app('think\App')->bind(Paginator::class, DxtcPage::class);
|
||||
$this->data["archives"] = Archives::getListPageByCategory($category['id'],$category['page_size']);
|
||||
return $this->view(empty($categoryTemplate)?"archives_default":$categoryTemplate);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param $categoryId 栏目id
|
||||
* @param $categoryTemplate 模板
|
||||
*/
|
||||
protected function page($categoryTemplate)
|
||||
{
|
||||
return $this->view(empty($categoryTemplate)?"page_default":$categoryTemplate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 文章详情
|
||||
*
|
||||
* @param $articleId
|
||||
*/
|
||||
public function archivesInfo()
|
||||
{
|
||||
$articleId = input("articleId/d",0);
|
||||
$archive = Archives::findOne([["id","=",$articleId]]);
|
||||
if(empty($archive)){
|
||||
return $this->error("内容不存在");
|
||||
}
|
||||
$archive->inc("views")->update();
|
||||
$this->data["archive"] = $archive;
|
||||
|
||||
|
||||
$category = ArchivesCategoryModel::findById($archive['category_id']);
|
||||
if(empty($category)){
|
||||
return $this->error("内容不存在");
|
||||
}
|
||||
$this->data["category"] = $category;
|
||||
$this->setActiveCategory($category['id']);
|
||||
$this->data['topCategoryId'] = ArchivesCategoryModel::firstGradeById($category['id']) ;
|
||||
|
||||
|
||||
//所有类型都要把碎片加上
|
||||
$this->data["blocks"] = Block:: getByCategoryId($category['id']);
|
||||
|
||||
|
||||
$seo_title = empty($archive['seo_title'])
|
||||
?
|
||||
$archive['title']
|
||||
:
|
||||
$archive['seo_title'];
|
||||
$seo_keywords = empty($archive['seo_keywords'])
|
||||
?
|
||||
""
|
||||
:
|
||||
$archive['seo_keywords'];
|
||||
$seo_description = empty($archive['seo_description'])
|
||||
?
|
||||
""
|
||||
:
|
||||
$archive['seo_description'];
|
||||
|
||||
|
||||
$this->setSeo( $seo_title,$seo_keywords,$seo_description);
|
||||
|
||||
|
||||
// 上一篇
|
||||
$this->data["prev"] = Archives::findOne([["category_id","=",$category['id']], ['sort', '<', $archive['sort']]]
|
||||
, [], function ($q) {
|
||||
return $q->with(["archivesCategory"])->order(['sort'=> 'desc']);
|
||||
});
|
||||
|
||||
// 下一篇
|
||||
$this->data["next"] = Archives::findOne([["category_id","=",$category['id']], ['sort', '>', $archive['sort']]]
|
||||
, [], function ($q) {
|
||||
return $q->with(["archivesCategory"])->order(['sort'=> 'asc']);
|
||||
});
|
||||
|
||||
return $this->view(!empty($category['detail_template'])?$category['detail_template']:"archives_default");
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace app\controller\api;
|
||||
|
||||
use app\model\AccountRecord;
|
||||
use app\model\Disease;
|
||||
use app\repository\ArchivesRepository;
|
||||
use Exception;
|
||||
use think\db\exception\DataNotFoundException;
|
||||
use think\db\exception\DbException;
|
||||
use think\db\exception\ModelNotFoundException;
|
||||
use think\response\Json;
|
||||
use app\exception\RepositoryException;
|
||||
|
||||
class Archives extends Base
|
||||
{
|
||||
protected $noNeedLogin = [];
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace app\controller\api;
|
||||
|
||||
use app\controller\BaseController;
|
||||
|
||||
/**
|
||||
* API控制器基础类
|
||||
*/
|
||||
class Base extends BaseController
|
||||
{
|
||||
// 布尔值数字关系
|
||||
public const BOOL_FALSE = 0;
|
||||
public const BOOL_TRUE = 1;
|
||||
|
||||
protected function initialize()
|
||||
{
|
||||
parent::initialize();
|
||||
|
||||
$this->middleware = [
|
||||
'jwt',
|
||||
'apiLogin' => ['except' => $this->noNeedLogin]
|
||||
];
|
||||
}
|
||||
|
||||
public function __call($method, $args)
|
||||
{
|
||||
return $this->json(4004, 'error request!');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
|
||||
namespace app\controller\api;
|
||||
|
||||
use app\exception\RepositoryException;
|
||||
use app\repository\AccountRepository;
|
||||
use app\repository\CommonRepository;
|
||||
use app\repository\OperationRepository;
|
||||
use app\repository\OrderRepository;
|
||||
use app\service\Sms;
|
||||
use app\validate\CommonValidate;
|
||||
use think\Collection;
|
||||
use think\db\exception\DataNotFoundException;
|
||||
use think\db\exception\DbException;
|
||||
use think\db\exception\ModelNotFoundException;
|
||||
use think\response\Json;
|
||||
|
||||
class Common extends Base
|
||||
{
|
||||
protected $noNeedLogin = [
|
||||
'slidePositions',
|
||||
'slides',
|
||||
];
|
||||
|
||||
/**
|
||||
* 发送短信验证码
|
||||
*
|
||||
* @OA\Post(
|
||||
* path="/common/send-code",
|
||||
* tags={"common"},
|
||||
* operationId="sendCode",
|
||||
* )
|
||||
*/
|
||||
public function sendCode(): Json
|
||||
{
|
||||
$input = input('post.');
|
||||
$validate = new CommonValidate();
|
||||
if (!$validate->scene('send_sms')->check($input)) {
|
||||
return $this->json(4001, '参数错误');
|
||||
}
|
||||
if (!in_array($input['type'], ['register', 'login', 'binding'])) {
|
||||
return $this->json(4002, '参数错误');
|
||||
}
|
||||
|
||||
CommonRepository::getInstance()->sendSms($input['phone'], $input['type']);
|
||||
return $this->json();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看轮播图可视位置配置信息
|
||||
*/
|
||||
public function slidePositions(): Json
|
||||
{
|
||||
$repo = OperationRepository::getInstance();
|
||||
$list = $repo->slidePositions();
|
||||
return $this->json(0, 'success', $list);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 轮播图
|
||||
*/
|
||||
public function slides(): Json
|
||||
{
|
||||
$size = $this->request->param('size/d', 0);
|
||||
$position = trim($this->request->param('position/s', ''));
|
||||
if (empty($position)) {
|
||||
return $this->json();
|
||||
}
|
||||
|
||||
try {
|
||||
$repo = OperationRepository::getInstance();
|
||||
$list = $repo->slideListByPosition($position, $size);
|
||||
} catch (\Exception $e) {
|
||||
$list = new Collection();
|
||||
}
|
||||
|
||||
return $this->json(0, 'success', $list);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
namespace app\controller\api;
|
||||
|
||||
use app\exception\RepositoryException;
|
||||
use app\job\NotifySms;
|
||||
use app\model\AccountFootmarks;
|
||||
use app\model\HotKeyword;
|
||||
use app\repository\AccountRepository;
|
||||
use app\service\ExtraConfig;
|
||||
use think\Collection;
|
||||
use think\facade\Config as CConfig;
|
||||
use think\facade\Queue;
|
||||
use think\response\Json;
|
||||
|
||||
class Index extends Base
|
||||
{
|
||||
protected $noNeedLogin = [];
|
||||
|
||||
public function index(): Json
|
||||
{
|
||||
return json(['code' => 0, 'msg' => 'I am index']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试用
|
||||
*
|
||||
* @return Json
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
public function test(): Json
|
||||
{
|
||||
$userId = $this->request->middleware('userInfo')['user_id'] ?? 0;
|
||||
$user = AccountRepository::getInstance()->info($userId, []);
|
||||
return json(['code' => 0, 'msg' => 'I am test ', 'data' => $user]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
namespace app\controller\api;
|
||||
|
||||
class User extends Base
|
||||
{
|
||||
protected $noNeedLogin = [];
|
||||
|
||||
public function index()
|
||||
{
|
||||
return json(['code' => 0, 'msg' => 'I am index']);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
<?php
|
||||
namespace app\controller\api\file;
|
||||
|
||||
use app\controller\api\Base;
|
||||
use app\model\File;
|
||||
use app\model\System;
|
||||
use app\service\Image;
|
||||
use app\validate\Upload as VUpload;
|
||||
use think\facade\Config;
|
||||
use think\facade\Filesystem;
|
||||
use think\facade\Lang;
|
||||
use think\response\Json;
|
||||
|
||||
/**
|
||||
* 文件上传
|
||||
*
|
||||
* Class Upload
|
||||
* @package app\controller\api\file
|
||||
*/
|
||||
class Upload extends Base
|
||||
{
|
||||
protected $noNeedLogin = [];
|
||||
|
||||
// 图片上传是否进行压缩[max-width:1920px]
|
||||
private bool $isCompress = true;
|
||||
private $validate = null;
|
||||
// 文件上传对外默认保存目录(相对路径)
|
||||
private string $uploadPath = '';
|
||||
// 文件上传对外默认保存目录是否有写权限
|
||||
private bool $uploadPathIsWritable = false;
|
||||
protected bool $saveToOos = false;
|
||||
|
||||
public function initialize()
|
||||
{
|
||||
parent::initialize();
|
||||
|
||||
$system = System::getSystem();
|
||||
if (!empty($system)) {
|
||||
$this->isCompress = $system['compress'] ?? true;
|
||||
}
|
||||
$this->validate = new VUpload();
|
||||
$this->uploadPath = Config::get('filesystem.disks.local.url');
|
||||
if(is_writable(app()->getRootPath() . 'public' . $this->uploadPath)){
|
||||
$this->uploadPathIsWritable = true;
|
||||
}
|
||||
|
||||
$this->cancelTimeLimit();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 通用文件上传
|
||||
* @return Json
|
||||
*/
|
||||
public function file()
|
||||
{
|
||||
$file = request()->file('file');
|
||||
if (empty($file)) {
|
||||
return $this->json(4001, '请上传的文件');
|
||||
}
|
||||
|
||||
if($this->validate->checkFile($file)){
|
||||
try{
|
||||
if(!$this->uploadPathIsWritable){
|
||||
throw new \Exception('上传文件夹需要写入权限');
|
||||
}
|
||||
$src = Filesystem::putFile('files/'.date('Ym'), $file, 'uniqid');
|
||||
$src = $this->uploadPath . '/' . $src;
|
||||
$return['src'] = $src;
|
||||
$return['name'] = $file->getOriginalName();
|
||||
|
||||
//加入上传文件表
|
||||
File::add($file, $src, $file->md5());
|
||||
} catch (\Exception $e) {
|
||||
return $this->json(4003, $e->getMessage());
|
||||
}
|
||||
|
||||
return $this->json(0,'success', $return);
|
||||
}else{
|
||||
|
||||
$errorMsg = Lang::get($this->validate->getError());
|
||||
return $this->json(4002, $errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用图片上传
|
||||
* @return Json
|
||||
*/
|
||||
public function image()
|
||||
{
|
||||
$image = request()->file('image');
|
||||
if (empty($image)) {
|
||||
return $this->json(4001, '请上传图片文件');
|
||||
}
|
||||
$md5 = $image->md5();//文件md5
|
||||
if($this->validate->checkImage($image)){
|
||||
try{
|
||||
if(!$this->uploadPathIsWritable){
|
||||
throw new \Exception('上传文件夹需要写入权限');
|
||||
}
|
||||
$src = Filesystem::putFile('images/'.date('Ym'), $image, 'uniqid');
|
||||
$src = $this->uploadPath . '/' . $src;
|
||||
$return['src'] = $src;
|
||||
if($this->isCompress){
|
||||
Image::resize($src);
|
||||
}
|
||||
|
||||
//加入上传文件表
|
||||
File::add($image, $src,$md5);
|
||||
} catch (\Exception $e) {
|
||||
return $this->json(4003, $e->getMessage());
|
||||
}
|
||||
|
||||
return $this->json(0, 'success', $return);
|
||||
}else{
|
||||
|
||||
$errorMsg = Lang::get($this->validate->getError());
|
||||
return $this->json(4002, $errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 同步到OOS服务器存储
|
||||
* @param string $src
|
||||
*/
|
||||
private function syncToOos(string $src)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,736 @@
|
|||
<?php
|
||||
|
||||
namespace app\controller\manager;
|
||||
|
||||
use app\exception\RepositoryException;
|
||||
use app\model\AccountDataLog;
|
||||
use app\model\AccountDataOperationLog;
|
||||
use app\model\AccountLevel;
|
||||
use app\model\AccountOperateLog;
|
||||
use app\model\AccountTagPivot;
|
||||
use app\model\CategoryConfig as CategoryConfigModel;
|
||||
use app\model\CustomerReceive;
|
||||
use app\model\AccountTag;
|
||||
use app\model\Account as AccountModel;
|
||||
use app\model\Order;
|
||||
use app\model\Staff;
|
||||
use app\model\Activity;
|
||||
use app\repository\AccountRepository;
|
||||
use app\repository\OrderRepository;
|
||||
use app\service\Math;
|
||||
use Exception;
|
||||
use think\db\exception\DataNotFoundException;
|
||||
use think\db\exception\DbException;
|
||||
use think\db\exception\ModelNotFoundException;
|
||||
use think\exception\ValidateException;
|
||||
use think\facade\Db;
|
||||
use think\facade\Log;
|
||||
use think\response\Json;
|
||||
use think\response\View;
|
||||
|
||||
/**
|
||||
* 用户管理
|
||||
*
|
||||
* Class Footmarks
|
||||
* @package app\controller\manager
|
||||
*/
|
||||
class Account extends Base
|
||||
{
|
||||
protected $noNeedLogin = ['getStaffList', 'getAccountList'];
|
||||
|
||||
/**
|
||||
* 详情
|
||||
*
|
||||
* @return View
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
public function detail(): View
|
||||
{
|
||||
$id = input('id/d', 0);
|
||||
$item = AccountRepository::getInstance()->findById($id, [], function ($q) {
|
||||
return $q->with(['serviceList']);
|
||||
});
|
||||
|
||||
$statusList = [
|
||||
Order::STATUS_SHIPPED, Order::STATUS_PAID, Order::STATUS_COMPLETED
|
||||
];
|
||||
$consumption = OrderRepository::getInstance()->userOrderList($id, [], 1, 0, $statusList);
|
||||
$orderNum = 0;
|
||||
$orderScoreNum = 0;
|
||||
$totalPrice = 0;
|
||||
$totalScore = 0;
|
||||
$totalCoin = 0;
|
||||
$consumption->each(function ($item) use (&$totalPrice, &$totalScore, &$totalCoin, &$orderScoreNum, &$orderNum) {
|
||||
if ($item->is_score == AccountModel::COMMON_ON) {
|
||||
$orderScoreNum += 1;
|
||||
} else {
|
||||
$orderNum += 1;
|
||||
}
|
||||
$totalPrice += $item->price;
|
||||
$totalScore += $item->score;
|
||||
$totalCoin += $item->coin;
|
||||
});
|
||||
$item['total_price'] = Math::fen2Yuan($totalPrice);
|
||||
$item['total_score'] = $totalScore;
|
||||
$item['total_coin'] = $totalCoin;
|
||||
$item['order_num'] = $orderNum;
|
||||
$item['order_score_num'] = $orderScoreNum;
|
||||
$item['order_newest'] = $consumption->toArray()[0] ?? [];
|
||||
$item['customer_service'] = $item->serviceList->name ?? '';
|
||||
$item['source_text'] = AccountRepository::getInstance()->getSourceDetail($id);
|
||||
$item['channel_text'] = AccountModel::channelTextList()[$item['channel']] ?? '';
|
||||
|
||||
$this->data['item'] = $item;
|
||||
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑
|
||||
*
|
||||
* @return Json|View
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function edit()
|
||||
{
|
||||
$id = input('id/d', 0);
|
||||
if (!$info = AccountRepository::getInstance()->findById($id)) {
|
||||
if ($this->request->isPost()) {
|
||||
return $this->json(4000, '用户不存在');
|
||||
} else {
|
||||
return $this->error('用户不存在');
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->request->isPost()) {
|
||||
$item = input('post.');
|
||||
$validate = $this->validateByApi($item, [
|
||||
'nickname' => 'require',
|
||||
]);
|
||||
|
||||
if ($validate !== true) {
|
||||
return $validate;
|
||||
}
|
||||
|
||||
try {
|
||||
$info->save($item);
|
||||
return $this->json();
|
||||
} catch (ValidateException $e) {
|
||||
return $this->json(4001, $e->getError());
|
||||
}
|
||||
}
|
||||
|
||||
$this->data['item'] = $info;
|
||||
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 单个字段编辑
|
||||
*
|
||||
* @return Json
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function modify(): Json
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$item = input('post.');
|
||||
$validate = $this->validateByApi($item, [
|
||||
'field' => 'require',
|
||||
'value' => 'require',
|
||||
]);
|
||||
|
||||
if ($validate !== true) {
|
||||
return $validate;
|
||||
}
|
||||
|
||||
if (!$info = AccountModel::findById($item['id'])) {
|
||||
return $this->json(4001, '记录不存在');
|
||||
}
|
||||
|
||||
$update = [$item['field'] => $item['value']];
|
||||
|
||||
try {
|
||||
$info->save($update);
|
||||
return $this->json();
|
||||
} catch (ValidateException $e) {
|
||||
return $this->json(4001, $e->getError());
|
||||
}
|
||||
}
|
||||
return $this->json(4000, '非法请求');
|
||||
}
|
||||
|
||||
/**
|
||||
* 列表
|
||||
*
|
||||
* @return View|Json
|
||||
* @throws Exception
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$page = input('page/d', 1);
|
||||
$size = input('size/d', 20);
|
||||
$searchParams = input('searchParams');
|
||||
$search = [];
|
||||
$other = [];
|
||||
if ($searchParams) {
|
||||
foreach ($searchParams as $key => $param) {
|
||||
if ($key == 'tag' && !empty($param)) {
|
||||
$other['tag_id'] = $param;
|
||||
continue;
|
||||
}
|
||||
if ($param || $param == '0') {
|
||||
$search[] = [$key, 'like', '%'.$param.'%'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$search[] = ['phone_active', '=', AccountModel::COMMON_ON];
|
||||
|
||||
// 后台绑定的账号
|
||||
$accountId = $this->auth['account_id'] ?? 0;
|
||||
|
||||
try {
|
||||
$items = AccountRepository::getInstance()->customerList($search, [], $accountId, $page, $size, function ($q) use ($other) {
|
||||
return $q->when(isset($other['tag_id']), function ($query) use ($other) {
|
||||
$query->leftJoin('account_tag_pivot atp', 'atp.account_id = id')->where('atp.tag_id', $other['tag_id']);
|
||||
});
|
||||
});
|
||||
return $this->json(0, '操作成功', $items);
|
||||
} catch (RepositoryException $e) {
|
||||
return $this->json(4001, $e->getMessage());
|
||||
} catch (Exception $e) {
|
||||
return $this->json(5001, '获取用户列表失败'.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
$this->data['channelList'] = AccountModel::channelTextList();
|
||||
$this->data['customerList'] = Staff::getCustomerServiceList();
|
||||
$this->data['tagList'] = AccountTag::getTags();
|
||||
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 分配员工
|
||||
*
|
||||
* @return View|Json
|
||||
* @throws Exception
|
||||
*/
|
||||
public function staff()
|
||||
{
|
||||
$id = input('id/s', '');
|
||||
if ($this->request->isPost()) {
|
||||
$ids = input('ids/s');
|
||||
$staffId = input('staff_id/d', 0);
|
||||
if (empty($ids)) {
|
||||
return $this->json(4001, '请选择要操作的用户');
|
||||
}
|
||||
|
||||
if (!$staffId) {
|
||||
return $this->json(4001, '请选择分配的员工');
|
||||
}
|
||||
|
||||
$ids = explode(',', $ids);
|
||||
|
||||
try {
|
||||
CustomerReceive::allotServiceByBatch($ids, $staffId);
|
||||
return $this->json(0, '操作成功');
|
||||
} catch (RepositoryException $e) {
|
||||
return $this->json(4001, $e->getMessage());
|
||||
} catch (Exception $e) {
|
||||
Log::error('分配指定员工失败'.$e->getMessage());
|
||||
return $this->json(5001, '分配指定员工失败');
|
||||
}
|
||||
}
|
||||
|
||||
$staffList = Staff::getCustomerServiceList();
|
||||
// 分配的是客服分组下所有客服
|
||||
$this->data['servicerList'] = json_encode($staffList, JSON_UNESCAPED_UNICODE);
|
||||
$this->data['id'] = $id;
|
||||
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 分配客户标签
|
||||
*
|
||||
* @return View|Json
|
||||
* @throws Exception
|
||||
*/
|
||||
public function tag()
|
||||
{
|
||||
$id = input('id/s', '');
|
||||
if ($this->request->isPost()) {
|
||||
$ids = input('ids/s');
|
||||
$tagId = input('tag_id/s');
|
||||
if (empty($ids)) {
|
||||
return $this->json(4001, '请选择要操作的用户');
|
||||
}
|
||||
|
||||
if (empty($tagId)) {
|
||||
return $this->json(4001, '请选择分配的标签');
|
||||
}
|
||||
|
||||
$ids = explode(',', $ids);
|
||||
$tags = explode(',', $tagId);
|
||||
|
||||
Db::startTrans();
|
||||
try {
|
||||
// 删除所有人标签
|
||||
AccountTagPivot::whereIn('account_id', $ids)->delete();
|
||||
// 新增标签
|
||||
$insert = [];
|
||||
foreach ($ids as $id) {
|
||||
foreach ($tags as $tag) {
|
||||
$arr = [];
|
||||
$arr['account_id'] = $id;
|
||||
$arr['tag_id'] = $tag;
|
||||
|
||||
$insert[] = $arr;
|
||||
}
|
||||
}
|
||||
|
||||
(new AccountTagPivot())->saveAll($insert);
|
||||
Db::commit();
|
||||
return $this->json(0, '操作成功');
|
||||
} catch (RepositoryException $e) {
|
||||
Db::rollback();
|
||||
return $this->json(4001, $e->getMessage());
|
||||
} catch (Exception $e) {
|
||||
Db::rollback();
|
||||
Log::error('分配客户标签失败'.$e->getMessage());
|
||||
return $this->json(5001, '分配客户标签失败');
|
||||
}
|
||||
}
|
||||
|
||||
$tagList = AccountTag::order('sort', 'desc')->order('id', 'asc')->select()->toArray();
|
||||
|
||||
// 分配的是线上客服
|
||||
$this->data['tagList'] = json_encode($tagList, JSON_UNESCAPED_UNICODE);
|
||||
$this->data['id'] = $id;
|
||||
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 分配客户来源
|
||||
*
|
||||
* @return View|Json
|
||||
* @throws Exception
|
||||
*/
|
||||
public function source()
|
||||
{
|
||||
$id = input('id/s', '');
|
||||
if ($this->request->isPost()) {
|
||||
$ids = input('ids/s');
|
||||
$channel = input('channel/s');
|
||||
if (empty($ids)) {
|
||||
return $this->json(4001, '请选择要操作的用户');
|
||||
}
|
||||
|
||||
if (empty($channel)) {
|
||||
return $this->json(4001, '请选择分配的客户来源');
|
||||
}
|
||||
|
||||
$value = $channel == AccountModel::CHANNEL_MEMBER ? input('type_staff') : input('type_'.$channel);
|
||||
$ids = explode(',', $ids);
|
||||
|
||||
Db::startTrans();
|
||||
try {
|
||||
$field = 'id,channel,inviter_account_id,inviter_parent_id,source_code';
|
||||
$accountList = AccountModel::whereIn('id', $ids)->column($field, 'id');
|
||||
$update = [];//更新account表
|
||||
$insert = [];//插入account_operate_log表
|
||||
|
||||
switch ($channel) {
|
||||
case AccountModel::CHANNEL_NORMAL:
|
||||
// 设为自然流量 清空上级邀请人和上上级邀请人
|
||||
$update = ['inviter_account_id' => 0, 'inviter_parent_id' => 0, 'channel' => $channel, 'source_code' => null];
|
||||
break;
|
||||
case AccountModel::CHANNEL_CUSTOMER:
|
||||
case AccountModel::CHANNEL_MEMBER:
|
||||
// 客户分享或员工分享 修改上级邀请人ID
|
||||
$update = ['inviter_account_id' => $value, 'channel' => $channel, 'source_code' => null];
|
||||
break;
|
||||
case AccountModel::CHANNEL_ACTIVITY:
|
||||
// 活码分享
|
||||
$update = ['inviter_account_id' => 0, 'source_code' => $value, 'channel' => $channel];
|
||||
break;
|
||||
}
|
||||
|
||||
$now = date('Y-m-d H:i:s');
|
||||
$createdBy = $this->auth['user_id'] ?? 0;
|
||||
foreach ($ids as $id) {
|
||||
$log = [];
|
||||
$originalSource = $accountList[$id] ?? [];
|
||||
switch ($channel) {
|
||||
case AccountModel::CHANNEL_NORMAL:
|
||||
$log['description'] = sprintf("从原始来源【channel=%s,inviter_account_id=%s,source_code=%s】变更为新来源【channel=%s,inviter_account_id=%s,source_code=%s】",
|
||||
$originalSource['channel'] ?? '', $originalSource['inviter_account_id'] ?? 0,
|
||||
$originalSource['source_code'] ?? '', $channel, 0, null);
|
||||
break;
|
||||
case AccountModel::CHANNEL_CUSTOMER:
|
||||
case AccountModel::CHANNEL_MEMBER:
|
||||
$log['description'] = sprintf("从原始来源【channel=%s,inviter_account_id=%s,source_code=%s】变更为新来源【channel=%s,inviter_account_id=%s,source_code=%s】",
|
||||
$originalSource['channel'] ?? '', $originalSource['inviter_account_id'] ?? 0,
|
||||
$originalSource['source_code'] ?? '', $channel, $value, null);
|
||||
break;
|
||||
case AccountModel::CHANNEL_ACTIVITY:
|
||||
$log['description'] = sprintf("从原始来源【channel=%s,inviter_account_id=%s,source_code=%s】变更为新来源【channel=%s,inviter_account_id=%s,source_code=%s】",
|
||||
$originalSource['channel'] ?? '', $originalSource['inviter_account_id'] ?? 0,
|
||||
$originalSource['source_code'] ?? '', $channel, 0, $value);
|
||||
break;
|
||||
}
|
||||
|
||||
$log['type'] = AccountOperateLog::TYPE_CHANGE_SOURCE;
|
||||
$log['account_id'] = $id;
|
||||
$log['created_at'] = $now;
|
||||
$log['created_by'] = $createdBy;
|
||||
$insert[] = $log;
|
||||
}
|
||||
|
||||
(new AccountOperateLog())->saveAll($insert);
|
||||
(new AccountModel())->whereIn('id', $ids)->save($update);
|
||||
Db::commit();
|
||||
return $this->json(0, '操作成功');
|
||||
} catch (RepositoryException $e) {
|
||||
Db::rollback();
|
||||
return $this->json(4001, $e->getMessage());
|
||||
} catch (Exception $e) {
|
||||
Db::rollback();
|
||||
Log::error('分配客户标签失败'.$e->getMessage());
|
||||
return $this->json(5001, '分配客户标签失败');
|
||||
}
|
||||
}
|
||||
|
||||
// 客服来源
|
||||
$this->data['channelList'] = AccountModel::channelTextList();
|
||||
$this->data['id'] = $id;
|
||||
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 到店设置
|
||||
*
|
||||
* @return View|Json
|
||||
* @throws Exception
|
||||
*/
|
||||
public function sign()
|
||||
{
|
||||
$id = input('id/s', '');
|
||||
if ($this->request->isPost()) {
|
||||
$ids = input('ids/s');
|
||||
$sign = input('sign/d');
|
||||
if (empty($ids)) {
|
||||
return $this->json(4001, '请选择要操作的用户');
|
||||
}
|
||||
|
||||
if (!in_array($sign, [AccountModel::COMMON_ON, AccountModel::COMMON_OFF])) {
|
||||
|
||||
return $this->json(4001, '请选择是否到店');
|
||||
}
|
||||
|
||||
$ids = explode(',', $ids);
|
||||
|
||||
Db::startTrans();
|
||||
try {
|
||||
(new AccountModel())->whereIn('id', $ids)->save(['is_sign' => $sign]);
|
||||
Db::commit();
|
||||
return $this->json(0, '操作成功');
|
||||
} catch (Exception $e) {
|
||||
Db::rollback();
|
||||
Log::error('是否到店操作失败'.$e->getMessage());
|
||||
return $this->json(5001, '是否到店操作失败');
|
||||
}
|
||||
}
|
||||
|
||||
$this->data['id'] = $id;
|
||||
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取员工列表
|
||||
*
|
||||
* @return Json
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException|Exception
|
||||
*/
|
||||
public function getStaffList(): Json
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$keyword = input('keyword/s', '');
|
||||
$type = input('type/s', '');//员工类型标识 如在线客服=customer-online
|
||||
$page = input('page/d', 1);
|
||||
$size = input('size/d', 0);
|
||||
$id = input('id/d', 0);
|
||||
|
||||
$relationIds = [];//已选记录
|
||||
if ($id > 0 && $activity = Activity::findById($id)) {
|
||||
$relationIds = explode(',', $activity['account_id']);
|
||||
}
|
||||
|
||||
$res = Staff::getStaff($type, $keyword, $page, $size);
|
||||
|
||||
if ($res['total'] > 0) {
|
||||
$res['list'] = $res['list']->toArray();
|
||||
foreach ($res['list'] as &$item) {
|
||||
if (count($relationIds) > 0 && in_array($item['id'], $relationIds)) {
|
||||
$item['selected'] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->json(0, '操作成功', $res);
|
||||
}
|
||||
return $this->json(4001, '非法请求');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户列表
|
||||
*
|
||||
* @return Json
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException|Exception
|
||||
*/
|
||||
public function getAccountList(): Json
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$keyword = input('keyword/s', '');
|
||||
$page = input('page/d', 1);
|
||||
$size = input('size/d', 10);
|
||||
$id = input('id', '');
|
||||
|
||||
$relationIds = explode(',', $id);//已选记录
|
||||
|
||||
$where = [];
|
||||
$where[] = ['is_staff', '=', AccountModel::COMMON_OFF];
|
||||
if (!empty($keyword)) {
|
||||
$where[] = ['nickname|real_name|mobile', 'like', '%'.$keyword.'%'];
|
||||
}
|
||||
|
||||
$res = AccountModel::findList($where, ['id', 'nickname', 'real_name', 'mobile'], $page, $size);
|
||||
|
||||
if ($res['total'] > 0 && $relationIds) {
|
||||
$res['list'] = $res['list']->toArray();
|
||||
foreach ($res['list'] as &$item) {
|
||||
$item['name_text'] = sprintf("昵称:%s;真实姓名:%s,手机号:%s", $item['nickname'], $item['real_name'], $item['mobile']);
|
||||
if (count($relationIds) > 0 && in_array($item['id'], $relationIds)) {
|
||||
$item['selected'] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->json(0, '操作成功', $res);
|
||||
}
|
||||
return $this->json(4001, '非法请求');
|
||||
}
|
||||
|
||||
/**
|
||||
* 充值孔雀币
|
||||
* */
|
||||
public function rechargeCoin()
|
||||
{
|
||||
$id = input('id/s');
|
||||
if ($this->request->isPost()) {
|
||||
$ids = input('ids/s');
|
||||
if (empty($ids)) {
|
||||
return $this->json("4003", "请选择用户");
|
||||
}
|
||||
$ids = explode(",", $ids);
|
||||
if (count($ids) > 1000) {
|
||||
return $this->json("4003", "一次最多选择1000条");
|
||||
}
|
||||
|
||||
$coin = input("coin/d", 1, "abs");
|
||||
|
||||
$Account = AccountRepository::getInstance()->getModel()
|
||||
->where("id", "in", $ids)->lock(true)
|
||||
->select();
|
||||
|
||||
Db::startTrans();
|
||||
try {
|
||||
AccountRepository::getInstance()->getModel()->where("id", "in", $ids)->inc("coin", $coin)->update();
|
||||
$time = date("Y-m-d H:i:s");
|
||||
$dataLog = [];
|
||||
$dataOperationLog = [];
|
||||
$Account->each(function ($item) use ($coin, $time, &$dataLog, &$dataOperationLog) {
|
||||
$dataLog[] = [
|
||||
"account_id" => $item["id"],
|
||||
"operator" => ($this->auth['nickname'] ?? ""),
|
||||
"operator_id" => $this->auth['user_id'] ?? 0,
|
||||
"name" => "后台充值孔雀币",
|
||||
"num" => $coin,
|
||||
"type" => AccountDataLog::TYPE_COIN,
|
||||
"action" => AccountDataLog::ACTION_ADMIN_RECHARGE,
|
||||
"created_at" => $time,
|
||||
"surplus" => ($item["coin"] + $coin),
|
||||
];
|
||||
$dataOperationLog[] = [
|
||||
"account_id" => $item["id"],
|
||||
"operator" => ($this->auth['nickname'] ?? ""),
|
||||
"num" => $coin,
|
||||
"remark" => "后台充值孔雀币",
|
||||
"type" => AccountDataLog::TYPE_COIN,
|
||||
"created_at" => $time,
|
||||
];
|
||||
});
|
||||
|
||||
AccountDataLog::insertAll($dataLog);
|
||||
AccountDataOperationLog::insertAll($dataOperationLog);
|
||||
|
||||
Db::commit();
|
||||
return $this->json();
|
||||
} catch (Exception $e) {
|
||||
Db::rollback();
|
||||
return $this->json("5003", "充值失败-1:".$e->getMessage());
|
||||
} catch (RepositoryException $e) {
|
||||
Db::rollback();
|
||||
return $this->json("5003", "充值失败-2:".$e->getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
$this->data['id'] = $id;
|
||||
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户等级管理
|
||||
* */
|
||||
public function accountLevel()
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$page = input('page/d', 1);
|
||||
$limit = input('size/d', 10);
|
||||
$items = AccountLevel::findList([], [], $page, $limit, null, ["value" => "desc"]);
|
||||
|
||||
$items["list"]->each(function (&$item) {
|
||||
if (empty($item["rights"])) {
|
||||
return;
|
||||
}
|
||||
$str = "";
|
||||
foreach (explode(",", $item["rights"]) as $ritem) {
|
||||
foreach (AccountLevel::$rightsArray as $mitem) {
|
||||
if ($mitem["key"] == $ritem) {
|
||||
$str .= $mitem["title"]." ";
|
||||
}
|
||||
}
|
||||
}
|
||||
$item->rights = $str;
|
||||
});
|
||||
|
||||
return $this->json(0, '操作成功', $items);
|
||||
}
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加用户等级管理
|
||||
* */
|
||||
public function addAccountLevel()
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$item = input('post.');
|
||||
$rule = [
|
||||
'name|名称' => 'require',
|
||||
'value|成长值' => 'require',
|
||||
];
|
||||
$validate = $this->validateByApi($item, $rule);
|
||||
if ($validate !== true) {
|
||||
return $validate;
|
||||
}
|
||||
try {
|
||||
AccountLevel::create($item);
|
||||
return $this->json();
|
||||
} catch (ValidateException $e) {
|
||||
return $this->json(4001, $e->getError());
|
||||
}
|
||||
}
|
||||
|
||||
//权益列表
|
||||
$rightsArray = AccountLevel::$rightsArray;
|
||||
$this->data['rightsJson'] = json_encode($rightsArray);
|
||||
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加用户等级管理
|
||||
* */
|
||||
public function delAccountLevel()
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$id = input('id/d');
|
||||
$item = AccountLevel::findById($id);
|
||||
if (empty($item)) {
|
||||
return $this->json(4001, "信息不存在");
|
||||
}
|
||||
AccountLevel::destroy($id);
|
||||
return $this->json();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑用户等级管理
|
||||
* */
|
||||
public function editAccountLevel()
|
||||
{
|
||||
$id = input("id/d");
|
||||
$info = AccountLevel::findById($id);
|
||||
if ($this->request->isPost()) {
|
||||
if (empty($info)) {
|
||||
return $this->json(4001, "信息不存在");
|
||||
}
|
||||
$item = $this->request->only(["name", "rights", "poster", "value", "content"]);
|
||||
$rule = [
|
||||
'name|名称' => 'require',
|
||||
'value|成长值' => 'require',
|
||||
];
|
||||
$validate = $this->validateByApi($item, $rule);
|
||||
if ($validate !== true) {
|
||||
return $validate;
|
||||
}
|
||||
try {
|
||||
AccountLevel::updateById($id, $item);
|
||||
return $this->json();
|
||||
} catch (ValidateException $e) {
|
||||
return $this->json(4001, $e->getError());
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($info)) {
|
||||
return $this->error("信息不存在");
|
||||
}
|
||||
|
||||
|
||||
$this->data['item'] = $info;
|
||||
|
||||
//权益列表
|
||||
$rightsArray = AccountLevel::$rightsArray;
|
||||
$selectd = [];
|
||||
if (!empty($info['rights'])) {
|
||||
$selectd = explode(",", $info['rights']);
|
||||
}
|
||||
$this->data['rightsJson'] = AccountLevel::xmSelectJson($selectd);
|
||||
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,306 @@
|
|||
<?php
|
||||
|
||||
namespace app\controller\manager;
|
||||
|
||||
use app\model\Disease;
|
||||
use app\model\DoctorRelation;
|
||||
use app\model\Archives as ArchivesModel;
|
||||
use app\model\ArchivesCategory as ArticleCategoryModel;
|
||||
use app\model\ArchivesModelField;
|
||||
use app\model\Spu;
|
||||
use app\model\Config;
|
||||
use app\repository\AccountRepository;
|
||||
use app\repository\CmsRepository;
|
||||
use Exception;
|
||||
use think\db\exception\DataNotFoundException;
|
||||
use think\db\exception\DbException;
|
||||
use think\db\exception\ModelNotFoundException;
|
||||
use think\exception\ValidateException;
|
||||
use think\response\Json;
|
||||
use think\response\View;
|
||||
|
||||
/**
|
||||
* 档案管理|内容管理
|
||||
*
|
||||
* Class Archives
|
||||
* @package app\controller\manager
|
||||
*/
|
||||
class Archives extends Base
|
||||
{
|
||||
protected $noNeedLogin = ['getDiseaseList', 'getDoctorList', 'getDiaryList', 'getCourseList'];
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*
|
||||
* @return Json
|
||||
*/
|
||||
public function del(): Json
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$ids = input('post.ids/a', []);
|
||||
if (empty($ids)) {
|
||||
$ids[] = input('post.id/d');
|
||||
}
|
||||
ArchivesModel::deleteByIds($ids);
|
||||
return $this->json();
|
||||
}
|
||||
return $this->json(4001, '非法请求!');
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑
|
||||
*
|
||||
* @return Json|View
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function edit()
|
||||
{
|
||||
$id = input('id/d', 0);
|
||||
|
||||
if (!$info = ArchivesModel::findById($id)) {
|
||||
return $this->json(4001, '记录不存在');
|
||||
}
|
||||
|
||||
if ($this->request->isPost()) {
|
||||
$item = input('post.');
|
||||
if (isset($item['video_src'])) {
|
||||
$item['video'] = $item['video_src'];
|
||||
unset($item['video_src']);
|
||||
}
|
||||
|
||||
$validate = $this->validateByApi($item, [
|
||||
'category_id|栏目' => 'require|gt:0',
|
||||
'title|标题' => 'require|max:255',
|
||||
'summary|摘要' => 'max:255',
|
||||
'content|内容' => 'require',
|
||||
], ['category_id.gt' => '请选择栏目']);
|
||||
|
||||
if ($validate !== true) {
|
||||
return $validate;
|
||||
}
|
||||
|
||||
try {
|
||||
$now = date('Y-m-d H:i:s');
|
||||
$item['updated_at'] = $now;
|
||||
$item['updated_by'] = $this->auth['user_id'];
|
||||
$info->save($item);
|
||||
return $this->json();
|
||||
} catch (ValidateException $e) {
|
||||
return $this->json(4001, $e->getError());
|
||||
}
|
||||
}
|
||||
|
||||
$showFieldList = ArchivesModelField::showFieldList();//所有栏目 可展示字段列表
|
||||
$currentShowFields = $showFieldList[$info['category_id']] ?? [];//当前选中栏目 可展示字段列表
|
||||
|
||||
$this->data['item'] = $info;
|
||||
$this->data['jsonList'] = $this->xmSelectJson([$info['category_id']]);
|
||||
$this->data['currentList'] = $currentShowFields;
|
||||
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 单个字段编辑
|
||||
*
|
||||
* @return Json
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function modify(): Json
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$item = input('post.');
|
||||
$validate = $this->validateByApi($item, [
|
||||
'field' => 'require',
|
||||
'value' => 'require',
|
||||
]);
|
||||
|
||||
if ($validate !== true) {
|
||||
return $validate;
|
||||
}
|
||||
|
||||
if (!$info = ArchivesModel::findById($item['id'])) {
|
||||
return $this->json(4001, '记录不存在');
|
||||
}
|
||||
|
||||
$update = [$item['field'] => $item['value']];
|
||||
|
||||
try {
|
||||
$info->save($update);
|
||||
return $this->json();
|
||||
} catch (ValidateException $e) {
|
||||
return $this->json(4001, $e->getError());
|
||||
}
|
||||
}
|
||||
return $this->json(4000, '非法请求');
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加
|
||||
*
|
||||
* @return Json|View
|
||||
* @throws Exception
|
||||
*/
|
||||
public function add()
|
||||
{
|
||||
$categoryId = input('category_id/d', 0);
|
||||
|
||||
if ($this->request->isPost()) {
|
||||
$item = input('post.');
|
||||
if (isset($item['video_src'])) {
|
||||
$item['video'] = $item['video_src'];
|
||||
unset($item['video_src']);
|
||||
}
|
||||
|
||||
$validate = $this->validateByApi($item, [
|
||||
'category_id|栏目' => 'require|gt:0',
|
||||
'title|标题' => 'require|max:255',
|
||||
'summary|摘要' => 'max:255',
|
||||
'content|内容' => 'require',
|
||||
], ['category_id.gt' => '请选择栏目']);
|
||||
|
||||
if ($validate !== true) {
|
||||
return $validate;
|
||||
}
|
||||
|
||||
try {
|
||||
$now = date('Y-m-d H:i:s');
|
||||
$item['created_at'] = $now;
|
||||
$item['published_at'] = $now;
|
||||
$item['created_by'] = $this->auth['user_id'];
|
||||
ArchivesModel::create($item);
|
||||
|
||||
return $this->json();
|
||||
} catch (ValidateException $e) {
|
||||
return $this->json(4001, $e->getError());
|
||||
}
|
||||
}
|
||||
|
||||
$showFieldList = ArchivesModelField::showFieldList();//所有栏目 可展示字段列表
|
||||
//有指定栏目获取指定栏目 否则获取第一个 可展示字段列表
|
||||
$currentShowFields = $categoryId > 0 ? ($showFieldList[$categoryId] ?? []) : array_values($showFieldList)[0];
|
||||
|
||||
$this->data['categoryId'] = $categoryId ?? 0;
|
||||
$this->data['diaryId'] = $diaryId ?? 0;
|
||||
$this->data['jsonList'] = $this->xmSelectJson([$categoryId]);
|
||||
$this->data['showList'] = json_encode($showFieldList, JSON_UNESCAPED_UNICODE);
|
||||
$this->data['currentList'] = $currentShowFields;
|
||||
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 列表
|
||||
*
|
||||
* @return View|Json
|
||||
* @throws Exception
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$categoryId = input('category_id/d', 0);
|
||||
if ($this->request->isPost()) {
|
||||
$page = input('page/d', 1);
|
||||
$limit = input('size/d', 20);
|
||||
$searchParams = input('searchParams');
|
||||
$where = [];
|
||||
|
||||
if ($categoryId > 0) {
|
||||
$where[] = ['category_id', '=', $categoryId];
|
||||
}
|
||||
|
||||
if ($searchParams) {
|
||||
foreach ($searchParams as $key => $param) {
|
||||
if (!empty($param)) {
|
||||
if (is_string($param)) {
|
||||
$where[] = [$key, 'like', '%'.$param.'%'];
|
||||
} elseif (is_array($param)) {
|
||||
//数组空元素去除
|
||||
foreach ($param as $k => $val) {
|
||||
if (empty($val)) {
|
||||
unset($param[$k]);
|
||||
}
|
||||
}
|
||||
if (!empty($param)) {
|
||||
$where[] = [$key, 'in', $param];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$items = ArchivesModel::findList($where, [], $page, $limit, function ($q) {
|
||||
return $q->with(['member', 'category'])
|
||||
->order('sort', 'desc')
|
||||
->order('id', 'desc');
|
||||
});
|
||||
|
||||
|
||||
|
||||
return $this->json(0, '操作成功', $items);
|
||||
}
|
||||
|
||||
$selected = $categoryId > 0 ? [$categoryId] : [];
|
||||
|
||||
$this->data['categoryId'] = $categoryId;
|
||||
$this->data['categoryJson'] = $this->categoryJson($selected);
|
||||
$this->data['archivesPath'] = '/'.Config::MINI_PATH_ARCHIVES;
|
||||
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造分类 json数据[zTree用]
|
||||
*
|
||||
* @param array $selected
|
||||
* @return false|string
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
*/
|
||||
private function categoryJson(array $selected = [])
|
||||
{
|
||||
$category = ArticleCategoryModel::order('sort', 'desc')
|
||||
->field('id,pid,title')
|
||||
->select()
|
||||
->toArray();
|
||||
foreach ($category as $k => $m) {
|
||||
$category[$k]['checked'] = in_array($m['id'], $selected);
|
||||
$category[$k]['spread'] = true;
|
||||
}
|
||||
|
||||
$category = CmsRepository::getInstance()->buildMenuChild(0, $category);
|
||||
return json_encode($category, JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 内容分类 构造xmSelect json数据[xmSelect用]
|
||||
*
|
||||
* @param array $selected
|
||||
* @param array $disabled
|
||||
* @return false|string
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
*/
|
||||
private function xmSelectJson(array $selected = [], array $disabled = [])
|
||||
{
|
||||
$category = ArticleCategoryModel::order('sort', 'desc')
|
||||
->field('id,pid,title')
|
||||
->select()
|
||||
->toArray();
|
||||
foreach ($category as $k => $m) {
|
||||
$category[$k]['selected'] = in_array($m['id'], $selected);
|
||||
$category[$k]['disabled'] = in_array($m['id'], $disabled);
|
||||
}
|
||||
|
||||
$category = CmsRepository::getInstance()->buildMenuChild(0, $category);
|
||||
$category = CmsRepository::getInstance()->handleSelectedList($category);
|
||||
return json_encode($category, JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,256 @@
|
|||
<?php
|
||||
|
||||
namespace app\controller\manager;
|
||||
|
||||
use app\repository\CmsRepository;
|
||||
use app\model\Log;
|
||||
use app\model\ArchivesCategory as ArchivesCategoryModel;
|
||||
|
||||
use app\validate\MenuValidate;
|
||||
use Exception;
|
||||
use think\facade\Cache;
|
||||
use think\facade\Db;
|
||||
use think\db\exception\DataNotFoundException;
|
||||
use think\db\exception\DbException;
|
||||
use think\db\exception\ModelNotFoundException;
|
||||
use think\exception\ValidateException;
|
||||
use think\response\Json;
|
||||
use think\response\View;
|
||||
|
||||
/**
|
||||
* 栏目管理
|
||||
*
|
||||
* Class Menu
|
||||
* @package app\controller\manager
|
||||
*/
|
||||
class ArchivesCategory extends Base
|
||||
{
|
||||
/**
|
||||
* 删除
|
||||
*
|
||||
* @return Json
|
||||
*/
|
||||
public function del(): Json
|
||||
{
|
||||
Cache::delete("categoryNames");//删除缓存
|
||||
if ($this->request->isPost()) {
|
||||
$ids = input('post.ids/a', []);
|
||||
if (empty($ids)) {
|
||||
$ids[] = input('post.id/d');
|
||||
}
|
||||
if (ArchivesCategoryModel::hasChildrenByIds($ids)) {
|
||||
return $this->json(4002, '待删除数据存在子数据');
|
||||
}
|
||||
|
||||
if (ArchivesCategoryModel::hasContentByIds($ids)) {
|
||||
return $this->json(4002, '待删除数据存在内容文章');
|
||||
}
|
||||
ArchivesCategoryModel::deleteByIds($ids);
|
||||
// Log::write(get_class().'Del', 'del', '涉及到的ID为:'.implode(',', $ids));
|
||||
return $this->json();
|
||||
}
|
||||
return $this->json(4001, '非法请求!');
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑
|
||||
*
|
||||
* @return Json|View
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function edit()
|
||||
{
|
||||
Cache::delete("categoryNames");//删除缓存
|
||||
$id = input('id/d', 0);
|
||||
|
||||
if (!$info = ArchivesCategoryModel::findById($id)) {
|
||||
return $this->json(4001, '记录不存在');
|
||||
}
|
||||
|
||||
if ($this->request->isPost()) {
|
||||
$item = input('post.');
|
||||
$validate = $this->validateByApi($item, [
|
||||
'pid|父级分类' => 'require|number',
|
||||
'model_id|所属模型' => 'require|number|gt:0',
|
||||
'title|标题' => 'require|max:100',
|
||||
'name|标识' => 'unique:archives_category,name,'.$info['id'] ?? 0,
|
||||
'description|描述' => 'max:255',
|
||||
], ['model_id' => '所属模型必需选择']);
|
||||
|
||||
if ($validate !== true) {
|
||||
return $validate;
|
||||
}
|
||||
|
||||
Db::startTrans();
|
||||
try {
|
||||
$oldPath = $info['path'] ?? '';
|
||||
$item['path'] = ArchivesCategoryModel::getPath($item['pid']);
|
||||
$info->save($item);
|
||||
|
||||
//刷新所有路径
|
||||
$oldPath = $oldPath.','.$id;
|
||||
$newPath = $item['path'].','.$id;
|
||||
if ($oldPath != $newPath) {
|
||||
ArchivesCategoryModel::refreshPath();
|
||||
}
|
||||
Db::commit();
|
||||
return $this->json();
|
||||
} catch (ValidateException $e) {
|
||||
Db::rollback();
|
||||
return $this->json(4001, $e->getError());
|
||||
}
|
||||
}
|
||||
|
||||
$disabled = ArchivesCategoryModel::getAllChildrenIds($id);
|
||||
$disabled[] = $id;
|
||||
$this->data['jsonList'] = $this->categoryJson([$info['pid']], $disabled);
|
||||
$this->data['modelList'] = $this->modelJson([$info['model_id']], []);
|
||||
$this->data['item'] = $info;
|
||||
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 单个字段编辑
|
||||
*
|
||||
* @return Json
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
*/
|
||||
public function modify(): Json
|
||||
{
|
||||
Cache::delete("categoryNames");//删除缓存
|
||||
if ($this->request->isPost()) {
|
||||
$item = input('post.');
|
||||
$validate = new MenuValidate();
|
||||
if (!$validate->scene('menu_modify')->check($item)) {
|
||||
return $this->json(4002, $validate->getError());
|
||||
}
|
||||
|
||||
if (!$info = ArchivesCategoryModel::findById($item['id'])) {
|
||||
return $this->json(4001, '记录不存在');
|
||||
}
|
||||
|
||||
$update = [$item['field'] => $item['value']];
|
||||
|
||||
try {
|
||||
$info->save($update);
|
||||
return $this->json();
|
||||
} catch (ValidateException $e) {
|
||||
return $this->json(4001, $e->getError());
|
||||
}
|
||||
}
|
||||
return $this->json(4000, '非法请求');
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加
|
||||
*
|
||||
* @return Json|View
|
||||
* @throws Exception
|
||||
*/
|
||||
public function add()
|
||||
{
|
||||
Cache::delete("categoryNames");//删除缓存
|
||||
if ($this->request->isPost()) {
|
||||
$item = input('post.');
|
||||
$validate = $this->validateByApi($item, [
|
||||
'pid|父级分类' => 'require|number',
|
||||
'model_id|所属模型' => 'require|number|gt:0',
|
||||
'title|标题' => 'require|max:100',
|
||||
'name|标识' => 'require|unique:archives_category',
|
||||
'description|描述' => 'max:255',
|
||||
], ['model_id' => '所属模型必需选择']);
|
||||
|
||||
if ($validate !== true) {
|
||||
return $validate;
|
||||
}
|
||||
try {
|
||||
$item['path'] = ArchivesCategoryModel::getPath($item['pid']);
|
||||
ArchivesCategoryModel::create($item);
|
||||
return $this->json();
|
||||
} catch (ValidateException $e) {
|
||||
return $this->json(4001, $e->getError());
|
||||
}
|
||||
}
|
||||
|
||||
$this->data['jsonList'] = $this->categoryJson();
|
||||
$this->data['modelList'] = $this->modelJson();
|
||||
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 列表
|
||||
*
|
||||
* @return Json|View
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$menus = ArchivesCategoryModel::getList();
|
||||
$res = [
|
||||
'code' => 0,
|
||||
'msg' => 'success',
|
||||
'count' => $menus->count(),
|
||||
'data' => $menus->toArray(),
|
||||
];
|
||||
return json($res);
|
||||
}
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $selected
|
||||
* @param array $disabled
|
||||
* @return false|string
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
*/
|
||||
private function categoryJson(array $selected = [], array $disabled = [])
|
||||
{
|
||||
$categoryList[] = ['title' => '顶级分类', 'id' => 0, 'disabled' => false, 'selected' => in_array(0, $selected)];
|
||||
$menus = ArchivesCategoryModel::getList();
|
||||
$menus = $menus->toArray();
|
||||
foreach ($menus as $k => $m) {
|
||||
$menus[$k]['selected'] = in_array($m['id'], $selected);
|
||||
$menus[$k]['disabled'] = in_array($m['id'], $disabled);
|
||||
}
|
||||
$menus = CmsRepository::getInstance()->buildMenuChild(0, $menus);
|
||||
$categoryList = array_merge($categoryList, CmsRepository::getInstance()->handleSelectedList($menus));
|
||||
return json_encode($categoryList, JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $selected
|
||||
* @param array $disabled
|
||||
* @return false|string
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
*/
|
||||
private function modelJson(array $selected = [], array $disabled = [])
|
||||
{
|
||||
$categoryList[] = ['title' => '全部', 'id' => 0, 'disabled' => false, 'selected' => in_array(0, $selected)];
|
||||
$menus = \app\model\ArchivesModel::field('id,0 as pid,title,name,sort,true as open')
|
||||
->order('sort', 'desc')
|
||||
->order('id', 'asc')
|
||||
->select();;
|
||||
$menus = $menus->toArray();
|
||||
foreach ($menus as $k => $m) {
|
||||
$menus[$k]['selected'] = in_array($m['id'], $selected);
|
||||
$menus[$k]['disabled'] = in_array($m['id'], $disabled);
|
||||
}
|
||||
$menus = CmsRepository::getInstance()->buildMenuChild(0, $menus);
|
||||
$categoryList = array_merge($categoryList, CmsRepository::getInstance()->handleSelectedList($menus));
|
||||
return json_encode($categoryList, JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,173 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace app\controller\manager;
|
||||
|
||||
use app\model\Log;
|
||||
use Exception;
|
||||
use app\model\ArchivesModel as MArchivesModel;
|
||||
use app\model\Archives;
|
||||
use app\model\ArchivesModelField;
|
||||
use think\exception\ValidateException;
|
||||
use think\facade\Db;
|
||||
use think\response\Json;
|
||||
use think\response\View;
|
||||
|
||||
/**
|
||||
* 文档模型管理
|
||||
* Class ArchivesModel
|
||||
* @package app\controller\manager
|
||||
*/
|
||||
class ArchivesModel extends Base
|
||||
{
|
||||
/**
|
||||
* 删除
|
||||
*
|
||||
* @return Json
|
||||
*/
|
||||
public function del(): Json
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$ids = input('post.ids/a', []);
|
||||
if (empty($ids)) {
|
||||
$ids[] = input('post.id/d');
|
||||
}
|
||||
$groupNames = MArchivesModel::whereIn('id', $ids)->column('name');
|
||||
if (ArchivesModelField::whereIn('name', $groupNames)->count() > 0) {
|
||||
return $this->json(4002, '模型下已存在字段,无法删除!');
|
||||
}
|
||||
|
||||
MArchivesModel::deleteByIds($ids);
|
||||
|
||||
// Log::write(get_class().'Del', 'del', '涉及到的ID为:'.implode(',', $ids));
|
||||
return $this->json();
|
||||
}
|
||||
return $this->json(4001, '非法请求!');
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑
|
||||
*
|
||||
* @return Json|View
|
||||
* @throws Exception
|
||||
*/
|
||||
public function edit()
|
||||
{
|
||||
$id = input('id/d', 0);
|
||||
|
||||
if (!$info = MArchivesModel::findById($id)) {
|
||||
return $this->json(4001, '记录不存在');
|
||||
}
|
||||
|
||||
if ($this->request->isPost()) {
|
||||
$item = input('post.');
|
||||
$validate = $this->validateByApi($item, [
|
||||
'title|模型标题' => 'require',
|
||||
'name|模型标识' => 'alphaDash|unique:archives_model,name,'.$id,
|
||||
]);
|
||||
|
||||
if ($validate !== true) {
|
||||
return $validate;
|
||||
}
|
||||
|
||||
try {
|
||||
ArchivesModelField::setFieldList($id, $info['name']);
|
||||
$info->save($item);
|
||||
return $this->json();
|
||||
} catch (ValidateException $e) {
|
||||
return $this->json(4001, $e->getError());
|
||||
}
|
||||
}
|
||||
|
||||
$this->data['item'] = $info;
|
||||
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 单个字段编辑
|
||||
*
|
||||
* @return Json
|
||||
* @throws Exception
|
||||
*/
|
||||
public function modify(): Json
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$item = input('post.');
|
||||
$validate = $this->validateByApi($item, [
|
||||
'field' => 'require',
|
||||
'value' => 'require',
|
||||
]);
|
||||
|
||||
if ($validate !== true) {
|
||||
return $validate;
|
||||
}
|
||||
|
||||
if (!$info = MArchivesModel::findById($item['id'])) {
|
||||
return $this->json(4001, '记录不存在');
|
||||
}
|
||||
|
||||
$update = [$item['field'] => $item['value']];
|
||||
|
||||
try {
|
||||
$info->save($update);
|
||||
return $this->json();
|
||||
} catch (ValidateException $e) {
|
||||
return $this->json(4001, $e->getError());
|
||||
}
|
||||
}
|
||||
return $this->json(4000, '非法请求');
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加
|
||||
*
|
||||
* @return Json|View
|
||||
* @throws Exception
|
||||
*/
|
||||
public function add()
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$item = input('post.');
|
||||
|
||||
$validate = $this->validateByApi($item, [
|
||||
'title|模型标题' => 'require',
|
||||
'name|模型标识' => 'require|alphaDash|unique:archives_model',
|
||||
]);
|
||||
|
||||
if ($validate !== true) {
|
||||
return $validate;
|
||||
}
|
||||
|
||||
try {
|
||||
$model = MArchivesModel::create($item);
|
||||
ArchivesModelField::setFieldList($model['id'], $model['name']);
|
||||
return $this->json();
|
||||
} catch (ValidateException $e) {
|
||||
return $this->json(4001, $e->getError());
|
||||
}
|
||||
}
|
||||
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 列表
|
||||
*
|
||||
* @return View|Json
|
||||
* @throws Exception
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$page = input('page/d', 1);
|
||||
$limit = input('size/d', 20);
|
||||
$items = MArchivesModel::findList([], [], $page, $limit, function ($q) {
|
||||
return $q->order('sort', 'desc')->order('id', 'asc');
|
||||
});
|
||||
|
||||
return $this->json(0, '操作成功', $items);
|
||||
}
|
||||
return $this->view();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,210 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace app\controller\manager;
|
||||
|
||||
use app\model\Log;
|
||||
use Exception;
|
||||
use app\model\ArchivesModelField as MArchivesModelField;
|
||||
use app\model\ArchivesModel;
|
||||
use think\exception\ValidateException;
|
||||
use think\response\Json;
|
||||
use think\response\View;
|
||||
|
||||
/**
|
||||
* 文档模型字段管理
|
||||
* Class ArchivesModelField
|
||||
* @package app\controller\manager
|
||||
*/
|
||||
class ArchivesModelField extends Base
|
||||
{
|
||||
/**
|
||||
* 删除
|
||||
*
|
||||
* @return Json
|
||||
*/
|
||||
public function del(): Json
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$ids = input('post.ids/a', []);
|
||||
if (empty($ids)) {
|
||||
$ids[] = input('post.id/d');
|
||||
}
|
||||
|
||||
MArchivesModelField::deleteByIds($ids);
|
||||
|
||||
// Log::write(get_class().'Del', 'del', '涉及到的ID为:'.implode(',', $ids));
|
||||
return $this->json();
|
||||
}
|
||||
return $this->json(4001, '非法请求!');
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑
|
||||
*
|
||||
* @return Json|View
|
||||
* @throws Exception
|
||||
*/
|
||||
public function edit()
|
||||
{
|
||||
$id = input('id/d', 0);
|
||||
|
||||
if (!$info = MArchivesModelField::findById($id)) {
|
||||
return $this->json(4001, '记录不存在');
|
||||
}
|
||||
|
||||
if ($this->request->isPost()) {
|
||||
$item = input('post.');
|
||||
$validate = $this->validateByApi($item, [
|
||||
'title|模型标题' => 'require',
|
||||
'name|模型标识' => 'alphaDash',
|
||||
]);
|
||||
|
||||
if ($validate !== true) {
|
||||
return $validate;
|
||||
}
|
||||
|
||||
try {
|
||||
$info->save($item);
|
||||
return $this->json();
|
||||
} catch (ValidateException $e) {
|
||||
return $this->json(4001, $e->getError());
|
||||
}
|
||||
}
|
||||
|
||||
$this->data['item'] = $info;
|
||||
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 单个字段编辑
|
||||
*
|
||||
* @return Json
|
||||
* @throws Exception
|
||||
*/
|
||||
public function modify(): Json
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$item = input('post.');
|
||||
$validate = $this->validateByApi($item, [
|
||||
'field' => 'require',
|
||||
'value' => 'require',
|
||||
]);
|
||||
|
||||
if ($validate !== true) {
|
||||
return $validate;
|
||||
}
|
||||
|
||||
if (!$info = MArchivesModelField::findById($item['id'])) {
|
||||
return $this->json(4001, '记录不存在');
|
||||
}
|
||||
|
||||
$update = [$item['field'] => $item['value']];
|
||||
|
||||
try {
|
||||
$info->save($update);
|
||||
return $this->json();
|
||||
} catch (ValidateException $e) {
|
||||
return $this->json(4001, $e->getError());
|
||||
}
|
||||
}
|
||||
return $this->json(4000, '非法请求');
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加
|
||||
*
|
||||
* @return Json|View
|
||||
* @throws Exception
|
||||
*/
|
||||
public function add()
|
||||
{
|
||||
$modelId = input('model_id/d', 0);
|
||||
|
||||
if ($this->request->isPost()) {
|
||||
$item = input('post.');
|
||||
$item['model_id'] = $modelId;
|
||||
|
||||
$validate = $this->validateByApi($item, [
|
||||
'title|字段标题' => 'require',
|
||||
'model_id|模型' => 'require|number|gt:0',
|
||||
'name|字段标识' => 'require|alphaDash|unique:archives_model_field',
|
||||
], ['model_id.gt' => '模型不存在']);
|
||||
|
||||
if ($validate !== true) {
|
||||
return $validate;
|
||||
}
|
||||
|
||||
try {
|
||||
MArchivesModelField::create($item);
|
||||
return $this->json();
|
||||
} catch (ValidateException $e) {
|
||||
return $this->json(4001, $e->getError());
|
||||
}
|
||||
}
|
||||
|
||||
$this->data['modelId'] = $modelId;
|
||||
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 列表
|
||||
*
|
||||
* @return View|Json
|
||||
* @throws Exception
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$modelId = input('model_id/d', 0);
|
||||
if (!$modelId) {
|
||||
return $this->json(4001, '请选择正确的模型');
|
||||
}
|
||||
|
||||
if ($this->request->isPost()) {
|
||||
$page = input('page/d', 1);
|
||||
$limit = input('size/d', 20);
|
||||
$where[] = ['model_id', '=', $modelId];
|
||||
$items = MArchivesModelField::findList($where, [], $page, $limit, function ($q) {
|
||||
return $q->order('status', 'desc')->order('id', 'asc');
|
||||
});
|
||||
|
||||
return $this->json(0, '操作成功', $items);
|
||||
}
|
||||
|
||||
$this->data['modelId'] = $modelId;
|
||||
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步字段
|
||||
*
|
||||
* @return Json
|
||||
* @throws Exception
|
||||
*/
|
||||
public function sync(): Json
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$modelId = input('model_id/d', 0);
|
||||
|
||||
if (!$modelId) {
|
||||
return $this->json(4001, '模型错误');
|
||||
}
|
||||
|
||||
|
||||
if (!$info = ArchivesModel::findOne(['id' => $modelId])) {
|
||||
return $this->json(4001, '模型不存在');
|
||||
}
|
||||
|
||||
try {
|
||||
MArchivesModelField::syncFieldList($modelId, $info['name']);
|
||||
return $this->json();
|
||||
} catch (ValidateException $e) {
|
||||
return $this->json(4001, $e->getError());
|
||||
}
|
||||
}
|
||||
return $this->json(4000, '非法请求');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,488 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace app\controller\manager;
|
||||
|
||||
use app\model\Log;
|
||||
use app\service\AliOss;
|
||||
use think\facade\Config;
|
||||
use Exception;
|
||||
use app\model\Attachment as AttachmentModel;
|
||||
use think\db\exception\DataNotFoundException;
|
||||
use think\db\exception\DbException;
|
||||
use think\db\exception\ModelNotFoundException;
|
||||
use think\exception\ValidateException;
|
||||
use think\response\Json;
|
||||
use think\response\View;
|
||||
|
||||
/**
|
||||
* 附件管理 - 素材管理
|
||||
* Class Attachment
|
||||
* @package app\controller\manager
|
||||
*/
|
||||
class Attachment extends Base
|
||||
{
|
||||
protected $noNeedLogin = ['file', 'getSize', 'md5List', 'pathDirHandle', 'toOss', 'delLostFile', 'test'];
|
||||
protected $DIRECTORY_SEPARATOR = "/";
|
||||
protected function initialize()
|
||||
{
|
||||
parent::initialize(); // TODO: Change the autogenerated stub
|
||||
$this->DIRECTORY_SEPARATOR = DIRECTORY_SEPARATOR == "\\" ? "/" : DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*
|
||||
* @return Json
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
*/
|
||||
public function del(): Json
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$ids = input('post.ids/a', []);
|
||||
if (empty($ids)) {
|
||||
$ids[] = input('post.id/d');
|
||||
}
|
||||
|
||||
$items = AttachmentModel::whereIn('id', $ids)->where('is_dir', AttachmentModel::COMMON_ON)->select();
|
||||
if ($items->where('is_dir', AttachmentModel::COMMON_ON)->count()) {
|
||||
$dirPaths = [];
|
||||
foreach ($items->toArray() as $item) {
|
||||
$dirPaths[] = $item['path'].$item['name']. $this->DIRECTORY_SEPARATOR ;
|
||||
}
|
||||
if (AttachmentModel::where('path', 'in', $dirPaths)->count()) {
|
||||
return $this->json(4001, '待删除目录下存在内容!');
|
||||
}
|
||||
}
|
||||
AttachmentModel::deleteByIds($ids);
|
||||
|
||||
// Log::write(get_class().'Del', 'del', '涉及到的ID为:'.implode(',', $ids));
|
||||
return $this->json();
|
||||
}
|
||||
return $this->json(4001, '非法请求!');
|
||||
}
|
||||
|
||||
/**
|
||||
* 单个字段编辑
|
||||
*
|
||||
* @return Json
|
||||
* @throws Exception
|
||||
*/
|
||||
public function modify(): Json
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$item = input('post.');
|
||||
$validate = $this->validateByApi($item, [
|
||||
'field' => 'require',
|
||||
'value' => 'require',
|
||||
]);
|
||||
|
||||
if ($validate !== true) {
|
||||
return $validate;
|
||||
}
|
||||
|
||||
if (!$info = AttachmentModel::findById($item['id'])) {
|
||||
return $this->json(4001, '记录不存在');
|
||||
}
|
||||
|
||||
if ($item['field'] == 'name' && $info['is_dir'] == AttachmentModel::COMMON_ON) {
|
||||
return $this->json(4002, '目录名称不能修改');
|
||||
|
||||
}
|
||||
|
||||
$update = [$item['field'] => $item['value']];
|
||||
|
||||
try {
|
||||
$info->save($update);
|
||||
return $this->json();
|
||||
} catch (ValidateException $e) {
|
||||
return $this->json(4001, $e->getError());
|
||||
}
|
||||
}
|
||||
return $this->json(4000, '非法请求');
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加
|
||||
*
|
||||
* @return Json|View
|
||||
* @throws Exception
|
||||
*/
|
||||
public function add()
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$item = input('post.');
|
||||
|
||||
$validate = $this->validateByApi($item, [
|
||||
'title|合集标题' => 'require',
|
||||
'user|虚拟用户' => 'require',
|
||||
'headimg|虚拟用户头像' => 'require',
|
||||
]);
|
||||
|
||||
if ($validate !== true) {
|
||||
return $validate;
|
||||
}
|
||||
|
||||
try {
|
||||
$now = date('Y-m-d H:i:s');
|
||||
$item['created_at'] = $now;
|
||||
$item['created_by'] = $this->auth['user_id'];
|
||||
AttachmentModel::create($item);
|
||||
return $this->json();
|
||||
} catch (ValidateException $e) {
|
||||
return $this->json(4001, $e->getError());
|
||||
}
|
||||
}
|
||||
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加文件夹
|
||||
*
|
||||
* @return Json|View
|
||||
* @throws Exception
|
||||
*/
|
||||
public function addFolder()
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$item = input('post.');
|
||||
|
||||
$validate = $this->validateByApi($item, [
|
||||
'name|文件夹名称' => 'require|alphaDash|max:20',
|
||||
'path|文件路径' => 'require',
|
||||
]);
|
||||
|
||||
// 例 name=dir4 path=/storage/dir1/dir2/dir3
|
||||
|
||||
// 去首尾/
|
||||
$path = trim($item['path'], $this->DIRECTORY_SEPARATOR );
|
||||
// 全路径 如 /storage/dir1/dir2/dir3/dir4/ 注意前后都有/
|
||||
$fullPath = $this->DIRECTORY_SEPARATOR .$path. $this->DIRECTORY_SEPARATOR .$item['name']. $this->DIRECTORY_SEPARATOR ;
|
||||
|
||||
if ($validate !== true) {
|
||||
return $validate;
|
||||
}
|
||||
|
||||
AttachmentModel::pathDirHandle($fullPath);
|
||||
return $this->json();
|
||||
}
|
||||
|
||||
$path = input('path/s', AttachmentModel::ROOT_PATH);
|
||||
|
||||
$this->data['path'] = $path;
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 图片列表
|
||||
*
|
||||
* @return View|Json
|
||||
* @throws Exception
|
||||
*/
|
||||
public function image()
|
||||
{
|
||||
$path = input('post.path', AttachmentModel::ROOT_PATH);
|
||||
$path = $this->DIRECTORY_SEPARATOR . trim($path, $this->DIRECTORY_SEPARATOR). $this->DIRECTORY_SEPARATOR;
|
||||
$path = str_replace("\\","/",$path);
|
||||
$type = input('type/s', 'image');
|
||||
$selected = input('selected', false);
|
||||
$multiple = input('multiple', false);
|
||||
|
||||
Config::load('extra/alioss', 'alioss');
|
||||
$config = config('alioss');
|
||||
$oss = $config['customDomain'];
|
||||
if ($this->request->isPost()) {
|
||||
$items = $this->list($path, ['image']);
|
||||
|
||||
return $this->json(0, '操作成功', $items);
|
||||
}
|
||||
|
||||
$this->data['path'] = $path;
|
||||
$this->data['oss'] = $oss;
|
||||
$this->data['type'] = $type;
|
||||
$this->data['multiple'] = $multiple;
|
||||
$this->data['selected'] = $selected;
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 视频列表
|
||||
*
|
||||
* @return View|Json
|
||||
* @throws Exception
|
||||
*/
|
||||
public function video()
|
||||
{
|
||||
$path = input('post.path', AttachmentModel::ROOT_PATH);
|
||||
$path = $this->DIRECTORY_SEPARATOR . trim($path, $this->DIRECTORY_SEPARATOR ) . $this->DIRECTORY_SEPARATOR ;
|
||||
$path = str_replace("\\","/",$path);
|
||||
$type = input('type/s', 'video');
|
||||
$selected = input('selected', false);
|
||||
$multiple = input('multiple', false);
|
||||
|
||||
Config::load('extra/alioss', 'alioss');
|
||||
$config = config('alioss');
|
||||
$oss = $config['customDomain'];
|
||||
if ($this->request->isPost()) {
|
||||
$items = $this->list($path, ['video']);
|
||||
|
||||
return $this->json(0, '操作成功', $items);
|
||||
}
|
||||
|
||||
$this->data['path'] = $path;
|
||||
$this->data['oss'] = $oss;
|
||||
$this->data['type'] = $type;
|
||||
$this->data['multiple'] = $multiple;
|
||||
$this->data['selected'] = $selected;
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件列表
|
||||
*
|
||||
* @return View|Json
|
||||
* @throws Exception
|
||||
*/
|
||||
public function file()
|
||||
{
|
||||
Config::load('extra/alioss', 'alioss');
|
||||
$config = config('alioss');
|
||||
$oss = $config['customDomain'];
|
||||
$type = input('type/s', 'all');
|
||||
$selected = input('selected', false);
|
||||
$multiple = input('multiple', false);
|
||||
|
||||
if ($this->request->isPost()) {
|
||||
$page = input('post.page', 1);
|
||||
$size = input('post.size', 20);
|
||||
$searchParams = input('searchParams');
|
||||
$where = [];
|
||||
|
||||
if ($searchParams) {
|
||||
foreach ($searchParams as $key => $param) {
|
||||
if (!empty($param)) {
|
||||
if (is_string($param)) {
|
||||
if ($key == 'is_oss') {
|
||||
if ($param >= 0) {
|
||||
$where[] = ['is_oss', '=', $param];
|
||||
}
|
||||
} else {
|
||||
$where[] = [$key, 'like', '%'.$param.'%'];
|
||||
}
|
||||
} elseif (is_array($param)) {
|
||||
//数组空元素去除
|
||||
foreach ($param as $k => $val) {
|
||||
if (empty($val)) {
|
||||
unset($param[$k]);
|
||||
}
|
||||
}
|
||||
if (!empty($param)) {
|
||||
$where[] = [$key, 'in', $param];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($type !== 'all') {
|
||||
$where[] = ['type', '=', $type];
|
||||
}
|
||||
|
||||
$items = AttachmentModel::findList($where, [], $page, $size, function ($q) {
|
||||
return $q->where('type', '<>', 'dir')->order('updated_at', 'desc');
|
||||
});
|
||||
|
||||
$items['list']->each(function ($item) {
|
||||
$item->size_text = getFilesize($item['size'] ?? 0);
|
||||
});
|
||||
|
||||
return $this->json(0, '操作成功', $items);
|
||||
}
|
||||
|
||||
$this->data['oss'] = $oss;
|
||||
$this->data['type'] = $type;
|
||||
$this->data['multiple'] = $multiple;
|
||||
$this->data['selected'] = $selected;
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 一键删除失效记录 即oss不存在&&本地不存在
|
||||
*
|
||||
* @return Json
|
||||
*/
|
||||
public function delLostFile(): Json
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$total = AttachmentModel::where('type', '<>', AttachmentModel::TYPE_DIR)
|
||||
->where('is_dir', AttachmentModel::COMMON_OFF)
|
||||
->where('is_oss', AttachmentModel::COMMON_OFF)
|
||||
->where('has_local', AttachmentModel::COMMON_OFF)
|
||||
->count();
|
||||
if ($total === 0) {
|
||||
return $this->json(0, 'success', ['total' => $total]);
|
||||
}
|
||||
|
||||
if (AttachmentModel::where('type', '<>', AttachmentModel::TYPE_DIR)
|
||||
->where('is_dir', AttachmentModel::COMMON_OFF)
|
||||
->where('is_oss', AttachmentModel::COMMON_OFF)
|
||||
->where('has_local', AttachmentModel::COMMON_OFF)
|
||||
->delete()) {
|
||||
return $this->json(0, 'success', ['total' => $total]);
|
||||
}
|
||||
return $this->json(4004, '删除失败');
|
||||
}
|
||||
return $this->json(4000, '请求错误');
|
||||
}
|
||||
|
||||
/**
|
||||
* 一键上传本地文件到OSS
|
||||
*
|
||||
* @return Json
|
||||
*/
|
||||
public function toOss(): Json
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
Config::load('extra/alioss', 'alioss');
|
||||
$config = config('alioss');
|
||||
|
||||
$ossObject = AliOss::instance();
|
||||
$bucket = $config['bucket'];
|
||||
|
||||
$total = AttachmentModel::where('type', '<>', AttachmentModel::TYPE_DIR)
|
||||
->where('is_dir', AttachmentModel::COMMON_OFF)
|
||||
->where('is_oss', AttachmentModel::COMMON_OFF)
|
||||
->field('id')
|
||||
->count();
|
||||
$done = 0;
|
||||
$none = 0;
|
||||
if ($total === 0) {
|
||||
return $this->json(0, 'success', ['total' => $total, 'done' => $done, 'none' => $none]);
|
||||
}
|
||||
try {
|
||||
AttachmentModel::where('type', '<>', AttachmentModel::TYPE_DIR)
|
||||
->where('is_dir', AttachmentModel::COMMON_OFF)
|
||||
->where('is_oss', AttachmentModel::COMMON_OFF)
|
||||
->field('id,src')
|
||||
->chunk(3, function ($items) use ($ossObject, $bucket, &$done, &$none) {
|
||||
$doneIds = [];
|
||||
$noneIds = [];
|
||||
foreach ($items as $item) {
|
||||
if ($item['src']) {
|
||||
$realPath = public_path().ltrim($item['src'], $this->DIRECTORY_SEPARATOR );
|
||||
if (!file_exists($realPath)) {
|
||||
$none++;
|
||||
$noneIds[] = $item['id'];
|
||||
continue;
|
||||
}
|
||||
$pathInfo = pathinfo($item['src']);
|
||||
$object = ltrim($item['src'], $this->DIRECTORY_SEPARATOR );
|
||||
//是否存在
|
||||
if (!$ossObject->doesObjectExist($bucket, $object)) {
|
||||
//创建目录
|
||||
$ossObject->createObjectDir($bucket, ltrim($pathInfo['dirname'], $this->DIRECTORY_SEPARATOR ));
|
||||
|
||||
$ossObject->uploadFile($bucket, $object, $realPath);
|
||||
}
|
||||
$doneIds[] = $item['id'];
|
||||
$done++;
|
||||
}
|
||||
}
|
||||
|
||||
// 失效标记
|
||||
if ($noneIds) {
|
||||
$update = ['is_oss' => AttachmentModel::COMMON_OFF, 'has_local' => AttachmentModel::COMMON_OFF];
|
||||
(new AttachmentModel())->where('id', 'in', $noneIds)->update($update);
|
||||
}
|
||||
|
||||
// 完成标记
|
||||
if ($doneIds) {
|
||||
$update = ['is_oss' => AttachmentModel::COMMON_ON];
|
||||
(new AttachmentModel())->where('id', 'in', $doneIds)->update($update);
|
||||
}
|
||||
});
|
||||
|
||||
return $this->json(0, 'success', ['total' => $total, 'done' => $done, 'none' => $none]);
|
||||
} catch (Exception $e) {
|
||||
\think\facade\Log::error('本地文件一键上传OSS失败 '.$e->getMessage());
|
||||
return $this->json(0, 'success', ['total' => $total, 'done' => $done, 'none' => $none]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定类型附件列表
|
||||
*
|
||||
* @param array $type
|
||||
* @param string $path
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function list(string $path, array $type): array
|
||||
{
|
||||
$type[] = 'dir';
|
||||
$where[] = ['path', '=', $path];
|
||||
$where[] = ['type', 'in', $type];
|
||||
$items = AttachmentModel::findList($where, [], 1, 0, function ($q) {
|
||||
return $q->order('is_dir', 'desc')->order('updated_at', 'desc');
|
||||
});
|
||||
$items['list']->each(function ($item) {
|
||||
$item->size_text = getFilesize($item['size'] ?? 0);
|
||||
});
|
||||
$items['path'] = $path;
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件大小
|
||||
*
|
||||
* @return Json
|
||||
*/
|
||||
public function getSize(): Json
|
||||
{
|
||||
$path = input('post.path', '');
|
||||
$types = input('post.type/a', []);
|
||||
|
||||
$size = '';
|
||||
if (empty($path)) {
|
||||
return $this->json(0, '操作成功', $size);
|
||||
}
|
||||
$path = str_replace("\\","/",$path);
|
||||
$total = AttachmentModel::where('path', 'like', $path.'%')
|
||||
->when(!empty($types), function ($q) use ($types) {
|
||||
$q->where('type', 'in', $types);
|
||||
})
|
||||
->sum('size');
|
||||
|
||||
return $this->json(0, '操作成功', getFilesize($total));
|
||||
}
|
||||
|
||||
// 将没有md5的文件 更新md5 仅针对本地文件
|
||||
public function md5List()
|
||||
{
|
||||
$noMd5List = AttachmentModel::whereNull('md5')->select();
|
||||
$update = [];
|
||||
foreach ($noMd5List as $item) {
|
||||
try {
|
||||
if (!empty($item['src'])) {
|
||||
$arr = [];
|
||||
$path = public_path().ltrim($item['src'], $this->DIRECTORY_SEPARATOR );
|
||||
$file = new \think\File($path);
|
||||
$arr['md5'] = $file->md5();
|
||||
$arr['id'] = $item['id'];
|
||||
|
||||
$update[] = $arr;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
(new AttachmentModel())->saveAll($update);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
<?php
|
||||
namespace app\controller\manager;
|
||||
|
||||
use think\facade\{Db, Config, Env};
|
||||
use app\model\{Log as MLog};
|
||||
use app\service\Tool;
|
||||
|
||||
class Backup extends Base
|
||||
{
|
||||
/**
|
||||
* 因受mysql 配置参数net_buffer_length和max_allowed_packet大小的限制,因此insert语句改为单条插入
|
||||
* show VARIABLES like "%net_buffer_length%" 默认单条sql语句长度16k
|
||||
* show VARIABLES like "%max_allowed_packet%" 默认单个sql文件最大容量
|
||||
* sql 文件注释 -- + 空格之后填写注释内容
|
||||
* 因根目录文件夹权限限制,因此需要手动创建backup/data目录并赋予权限(www用户组)
|
||||
*/
|
||||
public function back()
|
||||
{
|
||||
ini_set('max_execution_time', 0);
|
||||
ini_set("memory_limit",-1);
|
||||
set_time_limit(0);
|
||||
|
||||
$path = Config::get('filesystem.disks.backup.root') . '/';
|
||||
$dataBase = Env::get('database.database');
|
||||
// 不要使用单引号,双引号中的变量可以解析,单引号就是绝对的字符串
|
||||
$eol = "\r\n";
|
||||
$eolB = "\r\n\r\n";
|
||||
$info = '-- ------------------------------'.$eol;
|
||||
$info .= '-- 日期: '.date('Y-m-d H:i:s',time()).$eol;
|
||||
$info .= '-- MySQL --Database - '.$dataBase.$eol;
|
||||
$info .= '-- ------------------------------'.$eol;
|
||||
|
||||
$info .= 'CREATE DATABASE IF NOT EXISTS `'.$dataBase.'` DEFAULT CHARACTER SET "utf8mb4" COLLATE "utf8mb4_general_ci";'.$eolB;
|
||||
$info .= 'USE `'.$dataBase.'`;'.$eol;
|
||||
|
||||
if(is_dir($path)) {
|
||||
if(!is_writable($path)) {
|
||||
chmod($path, 0755);
|
||||
}
|
||||
} else {
|
||||
mkdir($path, 0755, true);
|
||||
}
|
||||
$fileName = $path.$dataBase.'_'.date('YmdHis',time()).'.sql';
|
||||
if(file_exists($fileName)) {
|
||||
@unlink($fileName);
|
||||
}
|
||||
// 以追加的方式写入
|
||||
file_put_contents($fileName, $info, FILE_APPEND);
|
||||
$tables = Db::query('show tables');
|
||||
foreach ($tables as $table) {
|
||||
// 表结构
|
||||
$tableName = $table['Tables_in_'.$dataBase];
|
||||
$sqlTable = 'SHOW CREATE TABLE '.$tableName;
|
||||
$res = Db::query($sqlTable);
|
||||
$infoTable = '-- ------------------------------'.$eol;
|
||||
$infoTable .= '-- Table structure for `'.$tableName.'`'.$eol;
|
||||
$infoTable .= '-- ------------------------------'.$eolB;
|
||||
$infoTable .= 'DROP TABLE IF EXISTS `'.$tableName.'`;'.$eolB;
|
||||
$infoTable .= $res[0]['Create Table'].';'.$eolB;
|
||||
// 表数据
|
||||
$infoTable .= '-- ------------------------------'.$eol;
|
||||
$infoTable .= '-- Data for the table `'.$tableName.'`'.$eol;
|
||||
$infoTable .= '-- ------------------------------'.$eolB;
|
||||
file_put_contents($fileName, $infoTable, FILE_APPEND);
|
||||
|
||||
$sqlData = 'select * from '.$tableName;
|
||||
$data = Db::query($sqlData);
|
||||
$count = count($data);
|
||||
if ($count > 0) {
|
||||
$dataStr = 'INSERT INTO `'.$tableName.'` VALUE ';
|
||||
foreach ($data as $k => $item) {
|
||||
$valStr = '(';
|
||||
foreach ($item as $val) {
|
||||
// 字符串转义
|
||||
$val = addslashes($val);
|
||||
$valStr .= '"'.$val.'", ';
|
||||
}
|
||||
// 去除最后的逗号和空格
|
||||
$valStr = substr($valStr,0,strlen($valStr)-2);
|
||||
// 限制单条sql语句在16K以内(可根据mysql配置条件进行调整)
|
||||
$sqlLength = strlen($dataStr) + strlen($valStr);
|
||||
if($sqlLength >= (1024 * 16 - 10)) {
|
||||
$dataStr .= $valStr.');'.$eol;
|
||||
file_put_contents($fileName, $dataStr, FILE_APPEND);
|
||||
// 提前分段写入后需重置数据语句
|
||||
if ($k <= ($count-1)) {
|
||||
$dataStr = 'INSERT INTO `'.$tableName.'` VALUE ';
|
||||
} else {
|
||||
$dataStr = '';
|
||||
}
|
||||
} else {
|
||||
if ($k < ($count-1)) {
|
||||
$dataStr .= $valStr.'),'.$eol;
|
||||
} else {
|
||||
$dataStr .= $valStr.');'.$eolB;
|
||||
}
|
||||
}
|
||||
}
|
||||
file_put_contents($fileName, $dataStr, FILE_APPEND);
|
||||
}
|
||||
}
|
||||
clearstatcache();
|
||||
|
||||
$backups = explode('/', $fileName);
|
||||
$backupName = $backups[count($backups)-1];
|
||||
$src = Config::get('filesystem.disks.backup.url') . '/';
|
||||
return $this->json(0, '备份成功:' . $src . $backupName);
|
||||
}
|
||||
|
||||
|
||||
public function index()
|
||||
{
|
||||
$path = Config::get('filesystem.disks.backup.root') . '/';
|
||||
$src = Config::get('filesystem.disks.backup.url') . '/';
|
||||
$items = [];
|
||||
if(is_dir($path)) {
|
||||
if(!is_readable($path)) {
|
||||
chmod($path,0755);
|
||||
}
|
||||
$files = scandir($path);
|
||||
foreach ($files as $file) {
|
||||
if($file != '.' && $file != '..') {
|
||||
$ext = substr($file, -4);
|
||||
if ($ext == '.sql') {
|
||||
$creatTime = substr($file, -18, 14);
|
||||
$items[] = [
|
||||
'file' => $file,
|
||||
'path' => $src . $file,
|
||||
'time' =>is_numeric($creatTime) ? date('Y-m-d H:i:s', strtotime($creatTime)) : '',
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
clearstatcache();
|
||||
|
||||
$this->data['items'] = $items;
|
||||
$this->data['backupPath'] = str_replace('\\','/', $path);
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
public function del()
|
||||
{
|
||||
if (request()->isPost()) {
|
||||
$filePath = input('post.id');
|
||||
Tool::delFile($filePath);
|
||||
MLog::write('backup', 'del', '删除了备份数据文件,文件路径为:' . $filePath);
|
||||
return $this->json();
|
||||
} else {
|
||||
return $this->json(1, '非法请求');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
<?php
|
||||
|
||||
namespace app\controller\manager;
|
||||
|
||||
use app\controller\BaseController;
|
||||
use app\service\File as FileTool;
|
||||
use Exception;
|
||||
use think\exception\ValidateException;
|
||||
use think\response\Json;
|
||||
use think\response\Redirect;
|
||||
use think\response\View;
|
||||
|
||||
/**
|
||||
* 控制器基础类
|
||||
*/
|
||||
class Base extends BaseController
|
||||
{
|
||||
protected $data = [];
|
||||
protected $auth = null;
|
||||
|
||||
protected function initialize()
|
||||
{
|
||||
$this->middleware = [
|
||||
'auth' => ['except' => array_merge($this->noNeedLogin, $this->noNeedRight)],
|
||||
'log'
|
||||
// 'jwt' => ['except' => $this->noNeedRight],
|
||||
];
|
||||
$this->auth = session('auth');
|
||||
$this->data['member'] = $this->auth;
|
||||
$this->data['_token'] = $this->auth['token'] ?? '';
|
||||
$this->data['groupId'] = $this->auth['groupId'] ?? 0;
|
||||
|
||||
$this->fileDomain();
|
||||
}
|
||||
|
||||
//变量赋值到模板
|
||||
protected function view(string $template = '')
|
||||
{
|
||||
return view($template)->assign($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $msg
|
||||
* @param string|null $url
|
||||
* @param string $data
|
||||
* @param int $wait
|
||||
* @return Redirect
|
||||
*/
|
||||
protected function error($msg = '', string $url = null, $data = '', int $wait = 3): Redirect
|
||||
{
|
||||
if (is_null($url)) {
|
||||
$url = $this->request->isAjax() ? '' : 'javascript:history.back(-1);';
|
||||
} elseif ($url) {
|
||||
$url = (strpos($url, '://') || 0 === strpos($url, '/')) ? $url : $this->app->route->buildUrl($url);
|
||||
}
|
||||
$result = [
|
||||
'code' => 0,
|
||||
'msg' => $msg,
|
||||
'data' => $data,
|
||||
'url' => $url,
|
||||
'wait' => $wait,
|
||||
];
|
||||
|
||||
return $this->redirect(url('/manager/error/jump', $result));
|
||||
}
|
||||
|
||||
public function __call($name, $args)
|
||||
{
|
||||
return $this->view('/manager/error/jump');
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证器
|
||||
*
|
||||
* @param array $data
|
||||
* @param $validate
|
||||
* @param array $message
|
||||
* @param bool $batch
|
||||
* @return Redirect
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function validateError(array $data, $validate, array $message = [], bool $batch = false): Redirect
|
||||
{
|
||||
try {
|
||||
parent::validate($data, $validate, $message, $batch);
|
||||
} catch (ValidateException $e) {
|
||||
$msg = $e->getMessage();
|
||||
if ($batch) {
|
||||
$msg = implode(',', $e->getError());
|
||||
}
|
||||
|
||||
return $this->error($msg);
|
||||
} catch (Exception $e) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件域名前缀
|
||||
*/
|
||||
public function fileDomain()
|
||||
{
|
||||
$this->data['fileDomain'] = FileTool::getFileDomain();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,224 @@
|
|||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | HisiPHP框架[基于ThinkPHP5.1开发]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016-2021 http://www.hisiphp.com
|
||||
// +----------------------------------------------------------------------
|
||||
// | HisiPHP承诺基础框架永久免费开源,您可用于学习和商用,但必须保留软件版权信息。
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 橘子俊 <364666827@qq.com>,开发者QQ群:50304283
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace app\controller\manager;
|
||||
use app\model\ArchivesCategory as ArticleCategoryModel;
|
||||
use app\model\ArchivesModelField;
|
||||
use app\model\Block as BlockModel;
|
||||
use app\model\System;
|
||||
use app\validate\Block as VBlock;
|
||||
use app\repository\CmsRepository;
|
||||
use think\db\exception\DataNotFoundException;
|
||||
use think\db\exception\DbException;
|
||||
use think\db\exception\ModelNotFoundException;
|
||||
use think\exception\ValidateException;
|
||||
|
||||
|
||||
/**
|
||||
* 碎片控制器
|
||||
* @package app\controller\cms
|
||||
*/
|
||||
class Block extends Base
|
||||
{
|
||||
|
||||
protected function initialize()
|
||||
{
|
||||
parent::initialize();
|
||||
$action = $this->request->action();
|
||||
$cid = $this->request->param('cid/d');
|
||||
if (($action == 'add' || $action == 'edit') && !$this->request->isPost()) {
|
||||
|
||||
$showFieldList = ArchivesModelField::showFieldList();//所有栏目 可展示字段列表
|
||||
$currentShowFields = $showFieldList[$cid] ?? [];//当前选中栏目 可展示字段列表
|
||||
|
||||
$this->data['system'] = System::getSystem();
|
||||
|
||||
$this->data['currentList'] = $currentShowFields;
|
||||
}
|
||||
$this->data['jsonList'] = $this->xmSelectJson([$cid]);
|
||||
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
if ($this->request->isAjax()) {
|
||||
$page = input('page/d', 1);
|
||||
$limit = input('size/d', 20);
|
||||
$keyword = input('searchParams.keyword/s');
|
||||
$categoryId = input('searchParams.category_id/d', 0);
|
||||
$where = [];
|
||||
|
||||
if ($categoryId > 0) {
|
||||
$where[] = ['category_id', '=', $categoryId];
|
||||
}
|
||||
|
||||
if (!empty($keyword)) {
|
||||
$where[] = ["name|title", 'like', '%'.$keyword.'%'];
|
||||
}
|
||||
|
||||
$items = BlockModel::findList($where, [], $page, $limit, function ($q) {
|
||||
return $q->with(["category"])->withAttr("content",function ($value,$data){
|
||||
switch ($data["type"]){
|
||||
case BlockModel::BLOCK://
|
||||
return $value;
|
||||
break;
|
||||
case BlockModel::TEXT:
|
||||
return $value;
|
||||
break;
|
||||
case BlockModel::IMG:
|
||||
return "<img src='{$value}'/>";
|
||||
break;
|
||||
case BlockModel::GROUP:
|
||||
$data = explode(",",$value);
|
||||
$str = "";
|
||||
foreach ($data as $vdata){
|
||||
$str.="<img src='{$vdata}'/>";
|
||||
}
|
||||
return $str;
|
||||
case BlockModel::FILE:
|
||||
return $value;
|
||||
break;
|
||||
case BlockModel::VIDEO:
|
||||
return $value;
|
||||
break;
|
||||
case BlockModel::ING_LIST:
|
||||
return $value;
|
||||
break;
|
||||
}
|
||||
});
|
||||
},["id"=>"desc"]);
|
||||
|
||||
return $this->json(0, '操作成功', $items);
|
||||
|
||||
}
|
||||
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
public function add(){
|
||||
if ($this->request->isPost()) {
|
||||
$postData = $this->request->post();
|
||||
try {
|
||||
$this->validate($postData, VBlock::class);
|
||||
$postData["content"] = $postData["content".$postData["type"]];
|
||||
|
||||
$hasWhere =[["name","=",$postData["name"]]] ;
|
||||
if( $postData["category_id"] > 0 ){
|
||||
$hasWhere[] = ["category_id","=",$postData["category_id"]];
|
||||
|
||||
}
|
||||
$other = BlockModel::findOne($hasWhere);
|
||||
if(!empty($other)){
|
||||
throw new ValidateException("键值重复");
|
||||
}
|
||||
}catch (ValidateException $e){
|
||||
return $this->json(4001,$e->getError());
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
|
||||
BlockModel::create($postData);
|
||||
return $this->json();
|
||||
}catch (\Exception $e){
|
||||
return $this->json(0,'保存失败'. $e->getMessage());
|
||||
}
|
||||
}
|
||||
$this->data["types"] = BlockModel::getTypes();
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
public function edit(){
|
||||
$id = input("id/d");
|
||||
$item = BlockModel::findById($id);
|
||||
if ($this->request->isPost()) {
|
||||
$postData = $this->request->post();
|
||||
try {
|
||||
$this->validate($postData, VBlock::class);
|
||||
$postData["content"] = $postData["content".$postData["type"]];
|
||||
$hasWhere =[["name","=",$postData["name"]],["id","<>",$id]] ;
|
||||
if( $postData["category_id"] > 0 ){
|
||||
$hasWhere[] = ["category_id","=",$postData["category_id"]];
|
||||
}else{
|
||||
$hasWhere[] = ["category_id","=",0];
|
||||
}
|
||||
$other = BlockModel::findOne($hasWhere);
|
||||
if(!empty($other)){
|
||||
throw new ValidateException("键值重复");
|
||||
}
|
||||
}catch (ValidateException $e){
|
||||
return $this->json(4001,$e->getError());
|
||||
}
|
||||
try {
|
||||
$item->save($postData);
|
||||
return $this->json();
|
||||
}catch (\Exception $e){
|
||||
return $this->json(0,'保存失败'. $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if(empty($item)){
|
||||
return $this->error("碎片不存在");
|
||||
}
|
||||
$item =$item->toArray();
|
||||
if($item['type'] == BlockModel::ING_LIST){
|
||||
$contentKey = array_keys($item['content']);
|
||||
$this->data['maxKey'] = max($contentKey)+1;
|
||||
}else{
|
||||
$this->data['maxKey'] = 0;
|
||||
}
|
||||
$this->data["item"] = $item;
|
||||
|
||||
$this->data["types"] = BlockModel::getTypes();
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*
|
||||
* @return Json
|
||||
*/
|
||||
public function del()
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$id = input('id/d', 0);
|
||||
BlockModel::deleteById($id);
|
||||
return $this->json();
|
||||
}
|
||||
return $this->json(4001, '非法请求!');
|
||||
}
|
||||
/**
|
||||
* 内容分类 构造xmSelect json数据[xmSelect用]
|
||||
*
|
||||
* @param array $selected
|
||||
* @param array $disabled
|
||||
* @return false|string
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
*/
|
||||
private function xmSelectJson(array $selected = [], array $disabled = [])
|
||||
{
|
||||
$category = ArticleCategoryModel::order('sort', 'desc')
|
||||
->field('id,pid,title')
|
||||
->select()
|
||||
->toArray();
|
||||
foreach ($category as $k => $m) {
|
||||
$category[$k]['selected'] = in_array($m['id'], $selected);
|
||||
$category[$k]['disabled'] = in_array($m['id'], $disabled);
|
||||
}
|
||||
|
||||
$category = CmsRepository::getInstance()->buildMenuChild(0, $category);
|
||||
$category = CmsRepository::getInstance()->handleSelectedList($category);
|
||||
return json_encode($category, JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace app\controller\manager;
|
||||
|
||||
use Exception;
|
||||
use think\facade\Config as CConfig;
|
||||
|
||||
/**
|
||||
* 额外配置
|
||||
* Class Config
|
||||
* @package app\controller\manager
|
||||
*/
|
||||
class Config extends Base
|
||||
{
|
||||
private string $extraPath = '';
|
||||
|
||||
protected function initialize()
|
||||
{
|
||||
parent::initialize();
|
||||
|
||||
$this->extraPath = config_path() . 'extra/';
|
||||
if (!is_dir($this->extraPath)) {
|
||||
if (is_writable(config_path())) {
|
||||
mkdir($this->extraPath, 0777, true);
|
||||
} else {
|
||||
halt('请联系系统管理人员配置文件夹读写权限!请添加'.$this->extraPath.'文件夹的读写权限');
|
||||
}
|
||||
} elseif (!is_writable($this->extraPath)) {
|
||||
halt('请联系系统管理人员配置文件夹读写权限!请添加'.$this->extraPath.'文件夹的读写权限');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function wechat()
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$data = input("post.");
|
||||
unset($data['_token']);
|
||||
$php = var_export($data, true);
|
||||
file_put_contents($this->extraPath . 'wechat.php', '<?php' . PHP_EOL . 'return ' . $php . ';');
|
||||
return $this->json();
|
||||
} else {
|
||||
CConfig::load('extra/wechat', 'wechat');
|
||||
$this->data['item'] = config('wechat');
|
||||
return $this->view();
|
||||
}
|
||||
}
|
||||
|
||||
public function alipay()
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$data = input("post.");
|
||||
unset($data['_token']);
|
||||
$php = var_export($data, true);
|
||||
file_put_contents($this->extraPath . 'alipay.php', '<?php' . PHP_EOL . 'return ' . $php . ';');
|
||||
return $this->json();
|
||||
} else {
|
||||
CConfig::load('extra/alipay', 'alipay');
|
||||
$this->data['item'] = config('alipay');
|
||||
return $this->view();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function __call($name, $args)
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
try {
|
||||
$data = input("post.");
|
||||
$php = var_export($data, true);
|
||||
file_put_contents(config_path().'extra/'.$name.'.php', '<?php'.PHP_EOL.'return '.$php.';');
|
||||
return $this->json();
|
||||
} catch (Exception $e) {
|
||||
return $this->json(4001, $e->getMessage());
|
||||
}
|
||||
} else {
|
||||
CConfig::load('extra/'.$name, $name);
|
||||
$this->data['item'] = config($name);
|
||||
$this->data['action'] = $name;
|
||||
return $this->view('manager/config/'.$name);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
namespace app\controller\manager;
|
||||
|
||||
class Error
|
||||
{
|
||||
public function jump()
|
||||
{
|
||||
$param = request()->param();
|
||||
return view()->assign($param);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
|
||||
namespace app\controller\manager;
|
||||
|
||||
use app\model\Feedback as FeedbackModel;
|
||||
use app\model\Log;
|
||||
use Exception;
|
||||
use think\response\Json;
|
||||
use think\response\View;
|
||||
|
||||
/**
|
||||
* 意见反馈
|
||||
*
|
||||
* Class Feedback
|
||||
* @package app\controller\manager
|
||||
*/
|
||||
class Feedback extends Base
|
||||
{
|
||||
/**
|
||||
* 删除
|
||||
*
|
||||
* @return Json
|
||||
*/
|
||||
public function del(): Json
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$ids = input('post.ids/a', []);
|
||||
if (empty($ids)) {
|
||||
$ids[] = input('post.id/d');
|
||||
}
|
||||
FeedbackModel::deleteByIds($ids);
|
||||
Log::write(get_class().'Del', 'del', '涉及到的ID为:'.implode(',', $ids));
|
||||
return $this->json();
|
||||
}
|
||||
return $this->json(4001, '非法请求!');
|
||||
}
|
||||
|
||||
/**
|
||||
* 列表
|
||||
*
|
||||
* @return View|Json
|
||||
* @throws Exception
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$page = input('page/d', 1);
|
||||
$limit = input('size/d', 20);
|
||||
$searchParams = input('searchParams', []);
|
||||
$search = [];
|
||||
if ($searchParams) {
|
||||
$searchParams = array_map('trim', $searchParams);
|
||||
if (isset($searchParams['user_keyword']) && !empty($searchParams['user_keyword'])) {
|
||||
$search[] = ['user_name|user_tel|user_email', 'like', '%'.$searchParams['user_keyword'].'%'];
|
||||
}
|
||||
unset($searchParams['user_keyword']);
|
||||
|
||||
foreach ($searchParams as $key => $param) {
|
||||
if ($param) {
|
||||
$search[] = [$key, 'like', '%'.$param.'%'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$items = FeedbackModel::findList($search, [], $page, $limit, function ($q) {
|
||||
return $q->with(['account'])->order('id', 'desc');
|
||||
});
|
||||
|
||||
$items['list'] = $items['list']->each(function ($item) {
|
||||
if ($item->account) {
|
||||
$item->user_name = empty($item->user_name) ? ($item->account->nickname ?? '') : $item->user_name;
|
||||
$item->user_tel = empty($item->user_tel) ? ($item->account->mobile ?? '') : $item->user_tel;
|
||||
}
|
||||
});
|
||||
|
||||
return $this->json(0, '操作成功', $items);
|
||||
}
|
||||
|
||||
|
||||
return $this->view();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
<?php
|
||||
namespace app\controller\manager;
|
||||
|
||||
use app\model\{File as MFile, Archives, Block, Category, Link, Slide, Log};
|
||||
use app\service\Tool;
|
||||
|
||||
class File extends Base
|
||||
{
|
||||
protected $noNeedLogin = ['delPath', 'del', 'unuse', 'getAllFilesInUse', 'list', 'index'];
|
||||
//删除磁盘上的文件
|
||||
public function delPath()
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$paths = input('post.paths/a');
|
||||
if(!empty($paths)){
|
||||
foreach($paths as $path){
|
||||
Tool::delFile($path);
|
||||
}
|
||||
Log::write('file', 'delPath', '批量删除了磁盘文件,涉及到的文件路径为:' . implode(',', $paths));
|
||||
return $this->json();
|
||||
}
|
||||
return $this->json(2, '待删除文件列表为空');
|
||||
}
|
||||
return $this->json(1, '非法请求!');
|
||||
}
|
||||
//删除文件记录
|
||||
public function del()
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$ids = input('post.ids/a');
|
||||
if(empty($ids) || !is_array($ids)) {
|
||||
return $this->json(2, '参数错误,请核对之后再操作!');
|
||||
}
|
||||
$items = MFile::getListByIds($ids);
|
||||
if(!empty($items)){
|
||||
$delIds = [];
|
||||
foreach($items as $item){
|
||||
$delIds[] = $item['id'];
|
||||
if($item['type'] == MFile::IMG){
|
||||
Tool::delFile($item['src'], 1);
|
||||
}else{
|
||||
Tool::delFile($item['src']);
|
||||
}
|
||||
}
|
||||
MFile::destroy($delIds);
|
||||
Log::write('file', 'del', '批量删除了文件,涉及到的文件ID为:' . implode(',', $delIds));
|
||||
return $this->json();
|
||||
}else{
|
||||
return $this->json(3, '待删除文件列表为空');
|
||||
}
|
||||
}
|
||||
return $this->json(1, '非法请求!');
|
||||
}
|
||||
/**
|
||||
* 未使用文件列表,
|
||||
* 1. 遍历数据库中使用的图片视频及文件路径
|
||||
* 2. 遍历上传目录中的文件
|
||||
* 3. 数据对比,找出存在目录中的文件&不在数据库中的文件
|
||||
* 4. 页面上显示查找出来的文件
|
||||
*/
|
||||
public function unuse()
|
||||
{
|
||||
$filesInUse = $this->getAllFilesInUse(); //数据库中在使用的文件
|
||||
$rootPath = app()->getRootPath();
|
||||
$uploadPath = $rootPath . 'storage';
|
||||
$uploadedFiles = getAllFilesByPath($uploadPath, $rootPath); //磁盘上上传的文件
|
||||
$files = MFile::getAll();
|
||||
$dbUploadedFiles = []; //数据库中上传的文件
|
||||
foreach($files as $file){
|
||||
$src = trim($file['src']);
|
||||
if(!empty($src)){
|
||||
$key = getKeyByPath($src);
|
||||
$dbUploadedFiles[$key] = $file;
|
||||
}
|
||||
}
|
||||
|
||||
$uploadedNotInUseFiles = array_diff_key($uploadedFiles, $filesInUse); //磁盘上上传未使用的文件
|
||||
$dbUploadedNotInUseFiles = array_diff_key($dbUploadedFiles, $filesInUse); //数据库中上传未使用的文件
|
||||
$bothNotInUseFiles = array_intersect_key($uploadedNotInUseFiles, $dbUploadedNotInUseFiles); //磁盘和数据库中,两边都未使用
|
||||
$this->data['uploadedNotInUseFiles'] = $uploadedNotInUseFiles;
|
||||
$this->data['dbUploadedNotInUseFiles'] = $dbUploadedNotInUseFiles;
|
||||
$this->data['bothNotInUseFilesKey'] = array_keys($bothNotInUseFiles);
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
//获取所有在使用的文件
|
||||
private function getAllFilesInUse()
|
||||
{
|
||||
$files = [];
|
||||
$blockFiles = Block::getFilesInUse();
|
||||
if(!empty($blockFiles)){
|
||||
$files = array_merge($files, $blockFiles);
|
||||
}
|
||||
$slideFiles = Slide::getFilesInUse();
|
||||
if(!empty($slideFiles)){
|
||||
$files = array_merge($files, $slideFiles);
|
||||
}
|
||||
$linkFiles = Link::getFilesInUse();
|
||||
if(!empty($linkFiles)){
|
||||
$files = array_merge($files, $linkFiles);
|
||||
}
|
||||
$categoryFiles = Category::getFilesInUse();
|
||||
if(!empty($categoryFiles)){
|
||||
$files = array_merge($files, $categoryFiles);
|
||||
}
|
||||
$articleFiles = Archives::getFilesInUse();
|
||||
if(!empty($articleFiles)){
|
||||
$files = array_merge($files, $articleFiles);
|
||||
}
|
||||
return $files;
|
||||
}
|
||||
|
||||
|
||||
//ajax获取文件列表
|
||||
public function list()
|
||||
{
|
||||
if($this->request->isAjax()){
|
||||
$page = input('param.page/d', 1);
|
||||
$size = input('param.size/d', 20);
|
||||
if(!is_integer($page) || $page < 1){
|
||||
$page = 1;
|
||||
}
|
||||
if (!is_integer($size) || $size < 1) {
|
||||
$size = 20;
|
||||
}
|
||||
$type = input('param.type', '');
|
||||
if(!in_array($type, array_keys(MFile::getTypes()))){
|
||||
$type = '';
|
||||
}
|
||||
$items = MFile::getList($type, $page, $size);
|
||||
return $this->json(0, 'ok', $items);
|
||||
}
|
||||
return $this->json(1, '无此操作');
|
||||
}
|
||||
//列表
|
||||
public function index()
|
||||
{
|
||||
$items = MFile::getListPage();
|
||||
$this->data['items'] = $items;
|
||||
$this->data['types'] = MFile::getTypes();
|
||||
return $this->view();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
<?php
|
||||
|
||||
namespace app\controller\manager;
|
||||
|
||||
use app\model\AccountRecord;
|
||||
use app\model\Overview;
|
||||
use app\model\AccountRole;
|
||||
use app\model\Appointment;
|
||||
use app\model\Account;
|
||||
use app\repository\CmsRepository;
|
||||
use Exception;
|
||||
use think\db\exception\DataNotFoundException;
|
||||
use think\db\exception\DbException;
|
||||
use think\db\exception\ModelNotFoundException;
|
||||
use think\response\Json;
|
||||
use think\response\View;
|
||||
use app\model\Member;
|
||||
use app\model\Menu;
|
||||
|
||||
class Index extends Base
|
||||
{
|
||||
protected $noNeedLogin = ['index', 'init'];
|
||||
|
||||
/**
|
||||
* 后台初始页面 随后进入dashboard页面
|
||||
*
|
||||
* @return View
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
*/
|
||||
public function index(): View
|
||||
{
|
||||
$auth = session('auth');
|
||||
$this->data['user'] = Member::findById($auth['user_id'] ?? 0, ['id', 'username', 'nickname', 'mobile']);
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 控制台
|
||||
*
|
||||
* @return View
|
||||
* @throws Exception
|
||||
*/
|
||||
public function dashboard(): View
|
||||
{
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 菜单初始化
|
||||
*
|
||||
* @return Json
|
||||
*/
|
||||
public function init(): Json
|
||||
{
|
||||
$res = [];
|
||||
$res['homeInfo'] = ['title' => '控制台', 'href' => "manager/index/dashboard"];
|
||||
$res['logoInfo'] = ['title' => 'CMS管理后台', 'href' => "", 'image' => '/static/manager/image/logo1.png'];
|
||||
|
||||
$menus = CmsRepository::getInstance()->getMenuList(Menu::TYPE_MENU, Menu::SHOW_YES)->toArray();
|
||||
$userId = $this->auth['user_id'] ?? 0;
|
||||
$menus = CmsRepository::getInstance()->handMenuRule($userId, $menus);
|
||||
$menus = CmsRepository::getInstance()->buildMenuChild(0, $menus, 'child');
|
||||
$res['menuInfo'] = $menus;
|
||||
|
||||
return json($res);
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存清理
|
||||
*
|
||||
* @return Json
|
||||
*/
|
||||
public function clear(): Json
|
||||
{
|
||||
$res = ['code' => 1, 'msg' => '服务端清理缓存成功'];
|
||||
sleep(2);
|
||||
|
||||
return json($res);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
<?php
|
||||
namespace app\controller\manager;
|
||||
|
||||
use app\model\{Link as MLink, System, Log};
|
||||
use app\validate\Link as VLink;
|
||||
use think\exception\ValidateException;
|
||||
use think\facade\Cache;
|
||||
|
||||
class Link extends Base
|
||||
{
|
||||
//删除
|
||||
public function del()
|
||||
{
|
||||
Cache::delete('links');//删除缓存
|
||||
if ($this->request->isPost()) {
|
||||
$ids = input('post.ids/a');
|
||||
if(empty($ids) || !is_array($ids)) {
|
||||
return $this->json(2, '参数错误,请核对之后再操作!');
|
||||
}
|
||||
$items = MLink::getListByIds($ids);
|
||||
if(!empty($items)){
|
||||
$delIds = [];
|
||||
foreach($items as $item){
|
||||
$delIds[] = $item['id'];
|
||||
}
|
||||
MLink::destroy($delIds);
|
||||
Log::write('link', 'del', '删除了友情链接,涉及到的ID为:' . implode(',', $delIds));
|
||||
return $this->json();
|
||||
}else{
|
||||
return $this->json(3, '待删除友情链接为空');
|
||||
}
|
||||
}
|
||||
return $this->json(1, '非法请求!');
|
||||
}
|
||||
|
||||
//排序
|
||||
public function sort()
|
||||
{
|
||||
Cache::delete('links');//删除缓存
|
||||
if($this->request->isPost()){
|
||||
$id = input('post.id/d');
|
||||
$sort = input('post.sort/d');
|
||||
|
||||
$item = MLink::findById($id);
|
||||
if(empty($item)){
|
||||
return $this->json(3, '该友情链接信息不存在!');
|
||||
}
|
||||
$item->sort = $sort;
|
||||
$item->save();
|
||||
return $this->json();
|
||||
|
||||
}
|
||||
return $this->json(1, '非法请求!');
|
||||
}
|
||||
|
||||
//编辑
|
||||
public function edit()
|
||||
{
|
||||
Cache::delete('links');//删除缓存
|
||||
if($this->request->isPost()){
|
||||
$item = input('post.item/a');
|
||||
$id = input('post.id/d');
|
||||
$img = input('post.img');
|
||||
if(is_numeric($id) && $id > 0) {
|
||||
$link = MLink::findById($id);
|
||||
if(empty($link)) {
|
||||
return $this->json(2, '该友情链接信息不存在!');
|
||||
}
|
||||
if(!empty($img)){
|
||||
$item['src'] = $img;
|
||||
}
|
||||
try {
|
||||
validate(VLink::class)->check($item);
|
||||
$link->save( $item);
|
||||
Log::write('link', 'edit', "友情链接编辑,ID:{$id} ,标题:{$item['title']}");
|
||||
return $this->json();
|
||||
} catch (ValidateException $e) {
|
||||
return $this->json(3, $e->getError());
|
||||
}
|
||||
}
|
||||
return $this->json(1, '参数错误,请核对之后再操作!');
|
||||
} else {
|
||||
$id = input('param.id/d');
|
||||
$item = MLink::getById($id);
|
||||
$imgSize = System::getLinkImageSize();
|
||||
|
||||
$this->data['item'] = $item;
|
||||
$this->data['img_size'] = $imgSize;
|
||||
return $this->view();
|
||||
}
|
||||
}
|
||||
|
||||
//添加
|
||||
public function add()
|
||||
{
|
||||
Cache::delete('links');//删除缓存
|
||||
if($this->request->isPost()){
|
||||
$item = input('post.item/a');
|
||||
$img = input('post.img');
|
||||
|
||||
if(!empty($img)){
|
||||
$item['src'] = $img;
|
||||
}
|
||||
try {
|
||||
validate(VLink::class)->check($item);
|
||||
$link = MLink::create($item);
|
||||
Log::write('link', 'add', "友情链接新增,ID:{$link->id} ,标题:{$item['title']}");
|
||||
return $this->json();
|
||||
} catch (ValidateException $e) {
|
||||
return $this->json(2, $e->getError());
|
||||
}
|
||||
} else {
|
||||
$imgSize = System::getLinkImageSize();
|
||||
$this->data['img_size'] = $imgSize;
|
||||
return $this->view();
|
||||
}
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$page = $this->request->param('page/d', 1);
|
||||
$size = $this->request->param('size/d', 30);
|
||||
|
||||
$whereMap = [];
|
||||
$orders = ['sort'=>'asc'];
|
||||
|
||||
$list = MLink::findList($whereMap, [], $page, $size, null, $orders);
|
||||
|
||||
return $this->json(0, 'success', $list);
|
||||
}
|
||||
return $this->view();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
namespace app\controller\manager;
|
||||
|
||||
use app\model\Log as LogModel;
|
||||
use Exception;
|
||||
use think\response\Json;
|
||||
use think\response\View;
|
||||
|
||||
/**
|
||||
* 日志
|
||||
*
|
||||
* Class Feedback
|
||||
* @package app\controller\manager
|
||||
*/
|
||||
class Log extends Base
|
||||
{
|
||||
/**
|
||||
* 列表
|
||||
*
|
||||
* @return View|Json
|
||||
* @throws Exception
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$page = input('page/d', 1);
|
||||
$limit = input('size/d', 20);
|
||||
$searchParams = input('searchParams');
|
||||
$search = [];
|
||||
if ($searchParams) {
|
||||
foreach ($searchParams as $key => $param) {
|
||||
if ($param) {
|
||||
if ($key == 'begin_time') {
|
||||
$begin = strtotime($param.' 00:00:00');
|
||||
$search[] = ['create_time', '>', $begin];
|
||||
} elseif ($key == 'end_time') {
|
||||
$end = strtotime($param.' 23:59:59');
|
||||
$search[] = ['create_time', '<', $end];
|
||||
} else {
|
||||
$search[] = [$key, 'like', '%'.$param.'%'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$items = LogModel::findList($search, [], $page, $limit, function ($q) {
|
||||
return $q->with(['memberName'])->order('create_time', 'desc');
|
||||
});
|
||||
|
||||
return $this->json(0, '操作成功', $items);
|
||||
}
|
||||
|
||||
return $this->view();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
|
||||
namespace app\controller\manager;
|
||||
|
||||
use app\service\Jwt;
|
||||
use Exception;
|
||||
use app\model\{Member, AuthRule, LoginLog};
|
||||
use app\controller\BaseController;
|
||||
use think\response\Json;
|
||||
use think\response\View;
|
||||
|
||||
class Login extends BaseController
|
||||
{
|
||||
protected $noNeedLogin = ['index'];
|
||||
|
||||
/**
|
||||
* @return View|Json
|
||||
* @throws Exception
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
if (request()->isPost()) {
|
||||
$param = input('post.data');
|
||||
$username = trim($param['username']);
|
||||
$password = trim($param['password']);
|
||||
$captcha = trim($param['captcha'] ?? '');
|
||||
if (!captcha_check($captcha)) {
|
||||
return $this->json(4001, '验证码错误'.$captcha);
|
||||
}
|
||||
|
||||
if (empty($username) || empty($password)) {
|
||||
return $this->json(4001, '用户名和密码不能为空');
|
||||
}
|
||||
$member = Member::getByUserName($username);
|
||||
if (empty($member)) {
|
||||
return $this->json(4002, '用户名或密码错误');
|
||||
}
|
||||
if ($member['password'] != md5($password.$username)) {
|
||||
return $this->json(4003, '用户名或密码错误');
|
||||
}
|
||||
if ($member['status'] != Member::STATUS_NORMAL) {
|
||||
return $this->json(4004, '账号已被禁用');
|
||||
}
|
||||
|
||||
$userInfo = [
|
||||
'user_id' => $member['id'],
|
||||
'username' => $member['username'],
|
||||
'nickname' => $member['nickname'],
|
||||
'account_id' => $member['account_id'],//绑定的前台用户ID
|
||||
];
|
||||
|
||||
$jwtToken = Jwt::generate($userInfo, env('app.expire', 7200));
|
||||
|
||||
$userInfo['token'] = $jwtToken;//jwt生成token
|
||||
|
||||
//记录最后登陆时间
|
||||
$ip = request()->ip();
|
||||
$time = time();
|
||||
Member::updateById($member['id'], [
|
||||
'login_time' => $time,
|
||||
'login_ip' => $ip
|
||||
]);
|
||||
LoginLog::create([
|
||||
'member_id' => $member['id'],
|
||||
'name' => $member['username'],
|
||||
'ip' => $ip,
|
||||
'create_time' => $time
|
||||
]);
|
||||
session('auth', $userInfo);
|
||||
return $this->json(0, 'success', ['url' => '/manager']);
|
||||
}
|
||||
|
||||
$viewData = [];
|
||||
return view()->assign($viewData);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
namespace app\controller\manager;
|
||||
|
||||
use app\controller\BaseController;
|
||||
use think\response\Redirect;
|
||||
|
||||
class Logout extends BaseController
|
||||
{
|
||||
protected $noNeedLogin = ['index'];
|
||||
|
||||
/**
|
||||
* 退出
|
||||
*
|
||||
* @return Redirect
|
||||
*/
|
||||
public function index(): Redirect
|
||||
{
|
||||
session(null);
|
||||
return redirect(url('/manager/login/index'));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,387 @@
|
|||
<?php
|
||||
|
||||
namespace app\controller\manager;
|
||||
|
||||
use app\model\Log;
|
||||
use app\model\Member as MemberModel;
|
||||
use app\model\Role as RoleModel;
|
||||
use Exception;
|
||||
use tauthz\facade\Enforcer;
|
||||
use think\db\exception\DataNotFoundException;
|
||||
use think\db\exception\DbException;
|
||||
use think\db\exception\ModelNotFoundException;
|
||||
use think\exception\ValidateException;
|
||||
use think\facade\Db;
|
||||
use think\response\Json;
|
||||
use think\response\Redirect;
|
||||
use think\response\View;
|
||||
|
||||
/**
|
||||
* (后台)人员管理
|
||||
*
|
||||
* Class Member
|
||||
* @package app\controller\manager
|
||||
*/
|
||||
class Member extends Base
|
||||
{
|
||||
/**
|
||||
* 删除
|
||||
*
|
||||
* @return Json
|
||||
*/
|
||||
public function del(): Json
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$ids = input('post.ids/a', []);
|
||||
if (empty($ids)) {
|
||||
$ids[] = input('post.id/d');
|
||||
}
|
||||
MemberModel::deleteByIds($ids);
|
||||
foreach ($ids as $id) {
|
||||
Enforcer::deleteRolesForUser($id);
|
||||
}
|
||||
Log::write(get_class().'Del', 'del', '涉及到的ID为:'.implode(',', $ids));
|
||||
return $this->json();
|
||||
}
|
||||
return $this->json(4001, '非法请求!');
|
||||
}
|
||||
|
||||
/**
|
||||
* 个人详情
|
||||
*
|
||||
* @return Json|View|Redirect
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function profile()
|
||||
{
|
||||
$id = $this->auth['user_id'] ?? 0;
|
||||
|
||||
if (!$item = MemberModel::findById($id)) {
|
||||
if ($this->request->isAjax()) {
|
||||
return $this->json(4001, '记录不存在');
|
||||
}
|
||||
return $this->error('记录不存在');
|
||||
}
|
||||
|
||||
if ($this->request->isPost()) {
|
||||
$post = input('post.');
|
||||
|
||||
$validate = $this->validateByApi($post, [
|
||||
'mobile|手机号' => 'require|unique:member,mobile,'.$id,
|
||||
'nickname|昵称' => 'require|chsAlphaNum|min:2|max:10',
|
||||
'remark|备注信息' => 'max:255',
|
||||
]);
|
||||
|
||||
if ($validate !== true) {
|
||||
return $validate;
|
||||
}
|
||||
|
||||
if (!checkMobile($post['mobile'])) {
|
||||
return $this->json(4002, '请输入正确的手机号码');
|
||||
}
|
||||
|
||||
try {
|
||||
$item->save($post);
|
||||
return $this->json();
|
||||
} catch (ValidateException $e) {
|
||||
return $this->json(4001, $e->getError());
|
||||
}
|
||||
}
|
||||
|
||||
$this->data['item'] = $item;
|
||||
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑
|
||||
*
|
||||
* @return Json|View
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function edit()
|
||||
{
|
||||
$id = input('id/d', 0);
|
||||
|
||||
if (!$info = MemberModel::findById($id)) {
|
||||
return $this->json(4001, '记录不存在');
|
||||
}
|
||||
|
||||
if ($this->request->isPost()) {
|
||||
$item = input('post.');
|
||||
|
||||
$validate = $this->validateByApi($item, [
|
||||
'mobile|手机号' => 'require|unique:member,mobile,'.$id,
|
||||
'nickname|昵称' => 'require|chsAlphaNum|min:2|max:10',
|
||||
'remark|备注信息' => 'max:255',
|
||||
]);
|
||||
|
||||
if ($validate !== true) {
|
||||
return $validate;
|
||||
}
|
||||
|
||||
if (!checkMobile($item['mobile'])) {
|
||||
return $this->json(4002, '请输入正确的手机号码');
|
||||
}
|
||||
|
||||
$roles = [];
|
||||
if ($item['roles']) {
|
||||
$roles = $item['roles'];
|
||||
$item['roles'] = implode(',', $item['roles']);
|
||||
}
|
||||
|
||||
Db::startTrans();
|
||||
try {
|
||||
$info->save($item);
|
||||
//删除所有角色
|
||||
Enforcer::deleteRolesForUser($id);
|
||||
//新增角色
|
||||
foreach ($roles as $role) {
|
||||
Enforcer::addRoleForUser($id, $role);
|
||||
}
|
||||
Db::commit();
|
||||
return $this->json();
|
||||
} catch (ValidateException $e) {
|
||||
Db::rollback();
|
||||
return $this->json(4001, $e->getError());
|
||||
}
|
||||
}
|
||||
|
||||
$this->data['item'] = $info;
|
||||
$this->data['roleJson'] = $this->roleJson(explode(',', $info['roles']));
|
||||
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 单个字段编辑
|
||||
*
|
||||
* @return Json
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function modify(): Json
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$item = input('post.');
|
||||
$validate = $this->validateByApi($item, [
|
||||
'field' => 'require',
|
||||
'value' => 'require',
|
||||
]);
|
||||
|
||||
if ($validate !== true) {
|
||||
return $validate;
|
||||
}
|
||||
|
||||
if (!$info = MemberModel::findById($item['id'])) {
|
||||
return $this->json(4001, '记录不存在');
|
||||
}
|
||||
|
||||
$update = [$item['field'] => $item['value']];
|
||||
|
||||
try {
|
||||
$info->save($update);
|
||||
return $this->json();
|
||||
} catch (ValidateException $e) {
|
||||
return $this->json(4001, $e->getError());
|
||||
}
|
||||
}
|
||||
return $this->json(4000, '非法请求');
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加
|
||||
*
|
||||
* @return Json|View
|
||||
* @throws Exception
|
||||
*/
|
||||
public function add()
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$item = input('post.');
|
||||
|
||||
$validate = $this->validateByApi($item, [
|
||||
'username|用户名' => 'require|alphaDash|min:4|max:16|unique:member',
|
||||
'mobile|手机号' => 'require|unique:member',
|
||||
'nickname|昵称' => 'require|chsAlphaNum|min:2|max:10',
|
||||
'password|密码' => 'require|min:4|max:16',
|
||||
'remark|备注信息' => 'max:255',
|
||||
]);
|
||||
|
||||
if ($validate !== true) {
|
||||
return $validate;
|
||||
}
|
||||
|
||||
if (!checkMobile($item['mobile'])) {
|
||||
return $this->json(4002, '请输入正确的手机号码');
|
||||
}
|
||||
|
||||
$roles = [];
|
||||
if ($item['roles']) {
|
||||
$roles = $item['roles'];
|
||||
$item['roles'] = implode(',', $item['roles']);
|
||||
}
|
||||
|
||||
Db::startTrans();
|
||||
try {
|
||||
$item['password'] = md5($item['password'].$item['username']);
|
||||
$member = MemberModel::create($item);
|
||||
foreach ($roles as $role) {
|
||||
Enforcer::addRoleForUser($member['id'], $role);
|
||||
}
|
||||
Db::commit();
|
||||
return $this->json();
|
||||
} catch (ValidateException $e) {
|
||||
Db::rollback();
|
||||
return $this->json(4001, $e->getError());
|
||||
}
|
||||
}
|
||||
|
||||
$this->data['roleJson'] = $this->roleJson();
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改密码
|
||||
*
|
||||
* @return Json|View|Redirect
|
||||
* @throws Exception
|
||||
*/
|
||||
public function password()
|
||||
{
|
||||
$id = input('id/d', 0);
|
||||
|
||||
if (!$item = MemberModel::findById($id)) {
|
||||
if ($this->request->isAjax()) {
|
||||
return $this->json(4001, '记录不存在');
|
||||
}
|
||||
return $this->error('记录不存在');
|
||||
}
|
||||
|
||||
if ($this->request->isPost()) {
|
||||
$post = input('post.');
|
||||
$validate = $this->validateByApi($post, [
|
||||
'password|密码' => 'require|confirm',
|
||||
]);
|
||||
|
||||
if ($validate !== true) {
|
||||
return $validate;
|
||||
}
|
||||
|
||||
$password = md5($post['password'].$item['username']);
|
||||
|
||||
try {
|
||||
$item->save(['password' => $password]);
|
||||
return $this->json();
|
||||
} catch (ValidateException $e) {
|
||||
return $this->json(4001, $e->getError());
|
||||
}
|
||||
}
|
||||
|
||||
$this->data['item'] = $item;
|
||||
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 个人修改密码
|
||||
*
|
||||
* @return Json|View
|
||||
* @throws Exception
|
||||
*/
|
||||
public function myPassword()
|
||||
{
|
||||
$id = $this->auth['user_id'] ?? 0;
|
||||
if (!$item = MemberModel::findById($id)) {
|
||||
return $this->json(4001, '记录不存在');
|
||||
}
|
||||
|
||||
if ($this->request->isPost()) {
|
||||
$post = input('post.');
|
||||
$validate = $this->validateByApi($post, [
|
||||
'old-password|旧密码' => 'require',
|
||||
'password|密码' => 'require|confirm',
|
||||
]);
|
||||
|
||||
if ($validate !== true) {
|
||||
return $validate;
|
||||
}
|
||||
|
||||
if ($item['password'] !== md5($post['old-password'].$item['username'])) {
|
||||
return $this->json(4002, '原始密码错误');
|
||||
}
|
||||
|
||||
$password = md5($post['password'].$item['username']);
|
||||
|
||||
try {
|
||||
$item->save(['password' => $password]);
|
||||
return $this->json();
|
||||
} catch (ValidateException $e) {
|
||||
return $this->json(4001, $e->getError());
|
||||
}
|
||||
}
|
||||
|
||||
$this->data['item'] = $item;
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 列表
|
||||
*
|
||||
* @return View|Json
|
||||
* @throws Exception
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$page = input('page/d', 1);
|
||||
$limit = input('size/d', 20);
|
||||
$searchParams = input('searchParams');
|
||||
$where = [];
|
||||
if ($searchParams) {
|
||||
foreach ($searchParams as $key => $param) {
|
||||
if (!empty($param)) {
|
||||
$where[] = [$key, 'like', '%'.$param.'%'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$items = MemberModel::findList($where, [], $page, $limit, function ($q) {
|
||||
return $q->order('id', 'desc');
|
||||
});
|
||||
|
||||
return $this->json(0, '操作成功', $items);
|
||||
}
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造角色json数据
|
||||
*
|
||||
* @param array $selected
|
||||
* @return false|string
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
*/
|
||||
private function roleJson(array $selected = [])
|
||||
{
|
||||
$roles = RoleModel::where('status', RoleModel::STATUS_NORMAL)
|
||||
->order('sort', 'desc')
|
||||
->select()
|
||||
->toArray();
|
||||
foreach ($roles as $k => $m) {
|
||||
$roles[$k]['checked'] = in_array($m['id'], $selected);
|
||||
$roles[$k]['spread'] = true;
|
||||
}
|
||||
return json_encode($roles, JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,259 @@
|
|||
<?php
|
||||
|
||||
namespace app\controller\manager;
|
||||
|
||||
use app\repository\CmsRepository;
|
||||
use app\model\Log;
|
||||
use app\model\Menu as MenuModel;
|
||||
|
||||
use app\validate\MenuValidate;
|
||||
use Exception;
|
||||
use think\db\exception\DataNotFoundException;
|
||||
use think\db\exception\DbException;
|
||||
use think\db\exception\ModelNotFoundException;
|
||||
use think\exception\ValidateException;
|
||||
use think\facade\Db;
|
||||
use think\response\Json;
|
||||
use think\response\View;
|
||||
|
||||
/**
|
||||
* 菜单管理
|
||||
*
|
||||
* Class Menu
|
||||
* @package app\controller\manager
|
||||
*/
|
||||
class Menu extends Base
|
||||
{
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*
|
||||
* @return Json
|
||||
*/
|
||||
public function del(): Json
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$ids = input('post.ids/a', []);
|
||||
if (empty($ids)) {
|
||||
$ids[] = input('post.id/d');
|
||||
}
|
||||
$repo = CmsRepository::getInstance();
|
||||
$repo->delMenuByIds($ids);
|
||||
return $this->json();
|
||||
}
|
||||
return $this->json(4001, '非法请求!');
|
||||
}
|
||||
|
||||
/**
|
||||
* 规则
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
private function rule(): array
|
||||
{
|
||||
return [
|
||||
'pid|父级菜单' => 'require|number',
|
||||
'title|标题' => 'require|max:100',
|
||||
'name|路由标识' => 'require',
|
||||
'remark|备注信息' => 'max:255',
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 编辑
|
||||
*
|
||||
* @return Json|View
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function edit()
|
||||
{
|
||||
$id = input('id/d', 0);
|
||||
|
||||
if (!$info = MenuModel::findById($id)) {
|
||||
return $this->json(4001, '记录不存在');
|
||||
}
|
||||
|
||||
if ($this->request->isPost()) {
|
||||
$item = input('post.');
|
||||
$validate = $this->validateByApi($item, $this->rule());
|
||||
if ($validate !== true) {
|
||||
return $validate;
|
||||
}
|
||||
|
||||
try {
|
||||
$oldPath = $info['path'];
|
||||
$item['path'] = MenuModel::getPath($item['pid']);
|
||||
$info->save($item);
|
||||
|
||||
//刷新所有路径
|
||||
$oldPath = $oldPath.','.$id;
|
||||
$newPath = $item['path'].','.$id;
|
||||
if ($oldPath != $newPath) {
|
||||
MenuModel::refreshPath();
|
||||
}
|
||||
return $this->json();
|
||||
} catch (ValidateException $e) {
|
||||
return $this->json(4001, $e->getError());
|
||||
}
|
||||
}
|
||||
|
||||
$disabled = MenuModel::getAllChildrenIds($id);
|
||||
$disabled[] = $id;
|
||||
$this->data['menuList'] = $this->menuJson([$info['pid']], $disabled);
|
||||
$this->data['item'] = $info;
|
||||
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 单个字段编辑
|
||||
*
|
||||
* @return Json
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
*/
|
||||
public function modify(): Json
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$item = input('post.');
|
||||
$validate = new MenuValidate();
|
||||
if (!$validate->scene('menu_modify')->check($item)) {
|
||||
return $this->json(4002, $validate->getError());
|
||||
}
|
||||
|
||||
if (!$info = MenuModel::findById($item['id'])) {
|
||||
return $this->json(4001, '记录不存在');
|
||||
}
|
||||
|
||||
$update = [$item['field'] => $item['value']];
|
||||
|
||||
try {
|
||||
$info->save($update);
|
||||
return $this->json();
|
||||
} catch (ValidateException $e) {
|
||||
return $this->json(4001, $e->getError());
|
||||
}
|
||||
}
|
||||
return $this->json(4000, '非法请求');
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加
|
||||
*
|
||||
* @return Json|View
|
||||
* @throws Exception
|
||||
*/
|
||||
public function add()
|
||||
{
|
||||
$id = input('id/d', 0);
|
||||
|
||||
if ($this->request->isPost()) {
|
||||
$item = input('post.');
|
||||
$validate = $this->validateByApi($item, $this->rule());
|
||||
if ($validate !== true) {
|
||||
return $validate;
|
||||
}
|
||||
try {
|
||||
$item['path'] = MenuModel::getPath($item['pid']);
|
||||
MenuModel::create($item);
|
||||
return $this->json();
|
||||
} catch (ValidateException $e) {
|
||||
return $this->json(4001, $e->getError());
|
||||
}
|
||||
}
|
||||
|
||||
$selected = $id > 0 ? [$id] : [];
|
||||
$this->data['menuList'] = $this->menuJson($selected);
|
||||
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 常规权限生成
|
||||
*
|
||||
* @return Json|View
|
||||
* @throws Exception
|
||||
*/
|
||||
public function generate()
|
||||
{
|
||||
$id = input('id/d', 0);
|
||||
|
||||
if ($this->request->isPost()) {
|
||||
$id = input('id/d', 0);
|
||||
if (!$item = MenuModel::findById($id)) {
|
||||
return $this->json(4002, '记录不存在');
|
||||
}
|
||||
|
||||
if ($item['type'] != MenuModel::TYPE_MENU) {
|
||||
return $this->json(4003, '仅菜单类型可操作');
|
||||
}
|
||||
|
||||
Db::startTrans();
|
||||
try {
|
||||
//自动生成常规操作
|
||||
MenuModel::generate($id, $item['name'], $item['path']);
|
||||
Db::commit();
|
||||
return $this->json();
|
||||
} catch (ValidateException $e) {
|
||||
Db::rollback();
|
||||
return $this->json(4001, $e->getError());
|
||||
}
|
||||
}
|
||||
|
||||
$selected = $id > 0 ? [$id] : [];
|
||||
$this->data['menuList'] = $this->menuJson($selected);
|
||||
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 列表
|
||||
*
|
||||
* @return View|Json
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$menus = CmsRepository::getInstance()->getMenuList();
|
||||
$menus->each(function ($item) {
|
||||
if ($item['type'] == 'menu') {
|
||||
$item->open = false;
|
||||
}
|
||||
});
|
||||
$res = [
|
||||
'code' => 0,
|
||||
'msg' => 'success',
|
||||
'count' => $menus->count(),
|
||||
'data' => $menus->toArray(),
|
||||
];
|
||||
return json($res);
|
||||
}
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* xmSelect插件 json数据
|
||||
*
|
||||
* @param array $selected
|
||||
* @param array $disabled
|
||||
* @return false|string
|
||||
*/
|
||||
private function menuJson(array $selected = [], array $disabled = [])
|
||||
{
|
||||
$categoryList[] = ['title' => '顶级菜单', 'id' => 0, 'prefix' => '', 'disabled' => false, 'open' => true, 'selected' => in_array(0, $selected)];
|
||||
$menus = CmsRepository::getInstance()->getMenuList();
|
||||
$menus = $menus->toArray();
|
||||
foreach ($menus as $k => $m) {
|
||||
$menus[$k]['selected'] = in_array($m['id'], $selected);
|
||||
$menus[$k]['disabled'] = in_array($m['id'], $disabled);
|
||||
}
|
||||
$menus = CmsRepository::getInstance()->buildMenuChild(0, $menus);
|
||||
$categoryList = array_merge($categoryList, CmsRepository::getInstance()->handleSelectedList($menus));
|
||||
return json_encode($categoryList, JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,270 @@
|
|||
<?php
|
||||
|
||||
namespace app\controller\manager;
|
||||
|
||||
use app\model\Log;
|
||||
use app\model\Menu;
|
||||
use app\model\Menu as MenuModel;
|
||||
use app\model\Role as RoleModel;
|
||||
use app\model\Rules;
|
||||
use app\repository\CmsRepository;
|
||||
use Exception;
|
||||
use think\db\exception\DataNotFoundException;
|
||||
use think\db\exception\DbException;
|
||||
use think\db\exception\ModelNotFoundException;
|
||||
use think\exception\ValidateException;
|
||||
use think\facade\Db;
|
||||
use think\response\Json;
|
||||
use think\response\View;
|
||||
|
||||
/**
|
||||
* 角色管理
|
||||
*
|
||||
* Class Role
|
||||
* @package app\controller\manager
|
||||
*/
|
||||
class Role extends Base
|
||||
{
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*
|
||||
* @return Json
|
||||
*/
|
||||
public function del(): Json
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$ids = input('post.ids/a', []);
|
||||
if (empty($ids)) {
|
||||
$ids[] = input('post.id/d');
|
||||
}
|
||||
RoleModel::deleteByIds($ids);
|
||||
Log::write(get_class().'Del', 'del', '涉及到的ID为:'.implode(',', $ids));
|
||||
return $this->json();
|
||||
}
|
||||
return $this->json(4001, '非法请求!');
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑
|
||||
*
|
||||
* @return Json|View
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function edit()
|
||||
{
|
||||
$id = input('id/d', 0);
|
||||
|
||||
if (!$info = RoleModel::findById($id)) {
|
||||
return $this->json(4001, '记录不存在');
|
||||
}
|
||||
|
||||
if ($this->request->isPost()) {
|
||||
$item = input('post.');
|
||||
$validate = $this->validateByApi($item, [
|
||||
'title' => 'require',
|
||||
]);
|
||||
|
||||
if ($validate !== true) {
|
||||
return $validate;
|
||||
}
|
||||
|
||||
try {
|
||||
$info->save($item);
|
||||
return $this->json();
|
||||
} catch (ValidateException $e) {
|
||||
return $this->json(4001, $e->getError());
|
||||
}
|
||||
}
|
||||
|
||||
$this->data['item'] = $info;
|
||||
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 单个字段编辑
|
||||
*
|
||||
* @return Json
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function modify(): Json
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$item = input('post.');
|
||||
$validate = $this->validateByApi($item, [
|
||||
'field' => 'require',
|
||||
'value' => 'require',
|
||||
]);
|
||||
|
||||
if ($validate !== true) {
|
||||
return $validate;
|
||||
}
|
||||
|
||||
if (!$info = RoleModel::findById($item['id'])) {
|
||||
return $this->json(4001, '记录不存在');
|
||||
}
|
||||
|
||||
$update = [$item['field'] => $item['value']];
|
||||
|
||||
try {
|
||||
$info->save($update);
|
||||
return $this->json();
|
||||
} catch (ValidateException $e) {
|
||||
return $this->json(4001, $e->getError());
|
||||
}
|
||||
}
|
||||
return $this->json(4000, '非法请求');
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加
|
||||
*
|
||||
* @return Json|View
|
||||
* @throws Exception
|
||||
*/
|
||||
public function add()
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$item = input('post.');
|
||||
|
||||
$validate = $this->validateByApi($item, [
|
||||
'title' => 'require',
|
||||
]);
|
||||
|
||||
if ($validate !== true) {
|
||||
return $validate;
|
||||
}
|
||||
|
||||
try {
|
||||
RoleModel::create($item);
|
||||
return $this->json();
|
||||
} catch (ValidateException $e) {
|
||||
return $this->json(4001, $e->getError());
|
||||
}
|
||||
}
|
||||
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 角色权限
|
||||
*
|
||||
* @return Json|View
|
||||
* @throws Exception
|
||||
*/
|
||||
public function rule()
|
||||
{
|
||||
$id = input('id/d', 0);
|
||||
|
||||
if (!$item = RoleModel::findById($id)) {
|
||||
return $this->json(4001, '记录不存在');
|
||||
}
|
||||
|
||||
if ($this->request->isPost()) {
|
||||
$ids = input('post.ids');
|
||||
$roleUpdate = $ids;//角色更新数据
|
||||
$ids = explode(',', $ids);
|
||||
|
||||
Db::startTrans();
|
||||
try {
|
||||
//查询角色已有权限
|
||||
$hasRules = Rules::where('ptype', 'p')->where('v0', $id)->select()->toArray();
|
||||
//角色最新权限列表
|
||||
$currentRules = MenuModel::where('id', 'in', $ids)->field('name')->select()->toArray();
|
||||
|
||||
foreach ($currentRules as &$rule) {
|
||||
$route = explode(':', $rule['name']);
|
||||
$v1 = $route[0];
|
||||
$v2 = $route[1] ?? 'index';
|
||||
|
||||
$rule['ptype'] = 'p';
|
||||
$rule['v0'] = $id;
|
||||
$rule['v1'] = $v1;
|
||||
$rule['v2'] = $v2;
|
||||
}
|
||||
|
||||
foreach ($hasRules as $k => $has) {
|
||||
foreach ($currentRules as $m => $current) {
|
||||
if ($has['ptype'] == $current['ptype'] && $has['v0'] == $current['v0'] && $has['v1'] == $current['v1'] && $has['v2'] == $current['v2']) {
|
||||
unset($currentRules[$m]);//删除当前权限列表已存在的 currentRules剩下的就是需要添加的记录
|
||||
unset($hasRules[$k]);//删除已有权限中存在的 hasRules剩下的就是需要删除的记录
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$insert = $currentRules;//需要添加的数据
|
||||
$delete = $hasRules;//需要删除的数据
|
||||
|
||||
$deleteIds = array_column($delete, 'id');//需要删除的ID
|
||||
(new Rules())->saveAll($insert);
|
||||
(new Rules())->where('id', 'in', $deleteIds)->delete();
|
||||
cache('tauthz', null);//权限缓存清空
|
||||
|
||||
$item->save(['rules' => $roleUpdate]);
|
||||
Db::commit();
|
||||
return $this->json();
|
||||
} catch (ValidateException $e) {
|
||||
Db::rollback();
|
||||
return $this->json(4001, $e->getError());
|
||||
}
|
||||
}
|
||||
|
||||
$selected = explode(',', $item['rules']);
|
||||
|
||||
$this->data['authJson'] = $this->authJson($selected);
|
||||
$this->data['item'] = $item;
|
||||
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造json数据
|
||||
*
|
||||
* @param array $selected
|
||||
* @return false|string
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
*/
|
||||
private function authJson(array $selected = [])
|
||||
{
|
||||
$menus = Menu::field("id,pid,title,sort")
|
||||
->where('status', Menu::STATUS_NORMAL)
|
||||
->order('sort', 'desc')
|
||||
->order('id', 'asc')
|
||||
->select()->toArray();
|
||||
foreach ($menus as $k => $m) {
|
||||
$menus[$k]['checked'] = in_array($m['id'], $selected);
|
||||
$menus[$k]['open'] = true;
|
||||
}
|
||||
$menus = CmsRepository::getInstance()->buildMenuChild(0, $menus);
|
||||
return json_encode($menus, JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 列表
|
||||
*
|
||||
* @return View|Json
|
||||
* @throws Exception
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$page = input('page/d', 1);
|
||||
$limit = input('size/d', 20);
|
||||
$items = RoleModel::findList([], [], $page, $limit, function ($q) {
|
||||
return $q->order('sort', 'desc')->order('id', 'asc');
|
||||
});
|
||||
|
||||
return $this->json(0, '操作成功', $items);
|
||||
}
|
||||
return $this->view();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
<?php
|
||||
namespace app\controller\manager;
|
||||
|
||||
use app\model\{AuthRule, AuthGroup, Log};
|
||||
use app\validate\AuthRule as VAuthRule;
|
||||
use Exception;
|
||||
use think\exception\ValidateException;
|
||||
use think\response\Json;
|
||||
|
||||
class Rule extends Base
|
||||
{
|
||||
/**
|
||||
* 权限排序
|
||||
* 暂不允许父级变更
|
||||
*
|
||||
* @return Json
|
||||
* @throws Exception
|
||||
*/
|
||||
public function sort()
|
||||
{
|
||||
if ($this->request->isAjax()) {
|
||||
$id = input('post.id');
|
||||
$sort = input('post.sort');
|
||||
$num = input('post.num/d', 1);
|
||||
if($num <= 0){
|
||||
$num = 1;
|
||||
}
|
||||
if(!in_array($sort, ['up', 'down'], true)){
|
||||
return $this->json(2, '参数错误');
|
||||
}
|
||||
$item = AuthRule::getById($id);
|
||||
if(empty($item)){
|
||||
return $this->json(3, '权限不存在');
|
||||
}
|
||||
if($sort == 'up'){
|
||||
$where = "parent_id = {$item['parent_id']} and sort < {$item['sort']}";
|
||||
$order = "sort desc";
|
||||
}else{
|
||||
$where = "parent_id = {$item['parent_id']} and sort > {$item['sort']}";
|
||||
$order = "sort asc";
|
||||
}
|
||||
$forSortItems = AuthRule::getListByWhereAndOrder($where, $order, $num);
|
||||
if(!empty($forSortItems)){
|
||||
$updateData = [];
|
||||
$forSortCount = count($forSortItems);
|
||||
for($i = 0; $i < $forSortCount; $i++){
|
||||
if($i == 0){
|
||||
$updateData[] = [
|
||||
'id' => $forSortItems[$i]['id'],
|
||||
'sort' => $item['sort']
|
||||
];
|
||||
}else{
|
||||
$updateData[] = [
|
||||
'id' => $forSortItems[$i]['id'],
|
||||
'sort' => $forSortItems[$i - 1]['sort']
|
||||
];
|
||||
}
|
||||
}
|
||||
$updateData[] = [
|
||||
'id' => $item['id'],
|
||||
'sort' => $forSortItems[$i - 1]['sort']
|
||||
];
|
||||
if(!empty($updateData)){
|
||||
$model = new AuthRule();
|
||||
$model->saveAll($updateData);
|
||||
$sortStr = $sort == 'up' ? '上移' : '下调';
|
||||
Log::write('rule', 'sort', "权限排序,ID:{$id} ,标题:{$item['title']},{$sortStr}了{$num}位");
|
||||
return $this->json();
|
||||
}
|
||||
}
|
||||
return $this->json(4, '无须调整排序!');
|
||||
}
|
||||
return $this->json(1, '非法请求!');
|
||||
}
|
||||
|
||||
/**
|
||||
* 权限删除
|
||||
*/
|
||||
public function del()
|
||||
{
|
||||
if ($this->request->isAjax()) {
|
||||
$ids = input('post.ids/a');
|
||||
$items = AuthRule::where('id', 'in', $ids)->select();
|
||||
if(!$items){
|
||||
return $this->json(1, '无此权限');
|
||||
}
|
||||
if(AuthRule::where('parent_id', 'in', $ids)->count()){
|
||||
return $this->json(2, '当前权限有下级权限,不可删除');
|
||||
}
|
||||
AuthRule::destroy($ids);
|
||||
AuthGroup::resetGroupRulesCache();
|
||||
$ids = implode(',', $ids);
|
||||
Log::write('rule', 'del', "权限删除,ID:{$ids}");
|
||||
return $this->json();
|
||||
}
|
||||
return $this->json(1, '非法请求!');
|
||||
}
|
||||
|
||||
/**
|
||||
* 权限修改
|
||||
*/
|
||||
public function edit()
|
||||
{
|
||||
if($this->request->isPost()){
|
||||
$item = input('post.item/a');
|
||||
$id = input('post.id');
|
||||
$rule = AuthRule::getById($id);
|
||||
if(empty($rule)){
|
||||
return $this->json(1, '请选择正确的权限');
|
||||
}
|
||||
$rule2 = AuthRule::getByName($item['name']);
|
||||
if(!empty($rule2) && $rule2['id'] != $id){
|
||||
return $this->json(2, '已存在相同权限['.$item['name'].']');
|
||||
}
|
||||
try {
|
||||
validate(VAuthRule::class)->check($item);
|
||||
AuthRule::updateById($id, $item);
|
||||
AuthGroup::resetGroupRulesCache();
|
||||
Log::write('rule', 'edit', "权限编辑,ID:{$id}, 标题:{$item['title']}");
|
||||
return $this->json();
|
||||
} catch (ValidateException $e) {
|
||||
return $this->json(3, $e->getError());
|
||||
}
|
||||
}
|
||||
$id = input('param.id/d');
|
||||
$rule = AuthRule::getById($id);
|
||||
if(empty($rule)){
|
||||
return $this->json(1,'无此权限信息,请核对之后再操作!');
|
||||
}else{
|
||||
$this->data['item'] = $rule;
|
||||
if($rule['parent_id'] > 0){
|
||||
$parent = AuthRule::getById($rule['parent_id']);
|
||||
$this->data['parent'] = $parent;
|
||||
}
|
||||
return $this->view();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 权限添加
|
||||
*/
|
||||
public function add()
|
||||
{
|
||||
if($this->request->isPost()){
|
||||
$item = input('post.item/a');
|
||||
try {
|
||||
validate(VAuthRule::class)->check($item);
|
||||
$rule = AuthRule::getByName($item['name']);
|
||||
if(!empty($rule)){
|
||||
return $this->json(1, '已存在相同权限');
|
||||
}
|
||||
$rule = AuthRule::create($item);
|
||||
//基本权限的话需要重置所有已有角色权限缓存
|
||||
if ($item['is_base'] > 0) {
|
||||
AuthGroup::resetGroupRulesCache();
|
||||
} else {
|
||||
AuthGroup::resetGroupRulesCache(1);
|
||||
}
|
||||
Log::write('rule', 'add', "权限新增,ID:{$rule->id}, 标题:{$item['title']}");
|
||||
return $this->json();
|
||||
} catch (ValidateException $e) {
|
||||
return $this->json(2, $e->getError());
|
||||
}
|
||||
}
|
||||
$parentId = input('param.parent_id/d',0);
|
||||
if($parentId > 0){
|
||||
$parent = AuthRule::getById($parentId);
|
||||
$this->data['parent'] = $parent;
|
||||
}
|
||||
$this->data['parentId'] = $parentId;
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 权限列表(全部)
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$list = AuthRule::getListTree();
|
||||
$this->data['items'] = $list;
|
||||
return $this->view();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,277 @@
|
|||
<?php
|
||||
|
||||
namespace app\controller\manager;
|
||||
|
||||
use app\model\Log;
|
||||
use app\repository\CmsRepository;
|
||||
use app\repository\OperationRepository;
|
||||
use app\validate\Slide as VSlide;
|
||||
use Exception;
|
||||
use think\db\exception\DataNotFoundException;
|
||||
use think\db\exception\DbException;
|
||||
use think\db\exception\ModelNotFoundException;
|
||||
use think\exception\ValidateException;
|
||||
use think\response\Json;
|
||||
use think\response\View;
|
||||
|
||||
class Slide extends Base
|
||||
{
|
||||
/**
|
||||
* 编辑
|
||||
*
|
||||
* @return Json|View
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
*/
|
||||
public function edit()
|
||||
{
|
||||
$id = $this->request->param('id/d', 0);
|
||||
|
||||
if (!$slide = OperationRepository::getInstance()->findSlideById($id)) {
|
||||
return $this->json(4001, '数据不存在');
|
||||
}
|
||||
|
||||
if ($this->request->isPost()) {
|
||||
$item = input('post.item/a');
|
||||
$validate = new VSlide();
|
||||
if (!$validate->scene('slide')->check($item)) {
|
||||
return $this->json(4002, $validate->getError());
|
||||
}
|
||||
|
||||
unset($item['id']);
|
||||
OperationRepository::getInstance()->updateSlide($item, $id);
|
||||
return $this->json();
|
||||
}
|
||||
|
||||
$this->data['item'] = $slide;
|
||||
$this->data['positionsJson'] = $this->xmSelectPositionsJson([$slide['position']]);
|
||||
$this->data['id'] = $id;
|
||||
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加
|
||||
*
|
||||
* @return View|Json
|
||||
*/
|
||||
public function add()
|
||||
{
|
||||
$repo = OperationRepository::getInstance();
|
||||
|
||||
if ($this->request->isPost()) {
|
||||
$item = input('post.item/a');
|
||||
$validate = new VSlide();
|
||||
if (!$validate->scene('slide')->check($item)) {
|
||||
return $this->json(4002, $validate->getError());
|
||||
}
|
||||
|
||||
$item['type'] = $item['type'] ?? 'img';
|
||||
$repo->createSlide($item);
|
||||
return $this->json();
|
||||
}
|
||||
|
||||
$this->data['positionsJson'] = $this->xmSelectPositionsJson();
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 轮播图列表
|
||||
*
|
||||
* @return Json|View
|
||||
* @throws Exception
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$repo = OperationRepository::getInstance();
|
||||
if ($this->request->isPost()) {
|
||||
$position = $this->request->param('position/s', '');
|
||||
$page = $this->request->param('page/d', 1);
|
||||
$size = $this->request->param('size/d', 30);
|
||||
|
||||
$whereMap = [];
|
||||
$orders = ['sort'=>'asc'];
|
||||
if (!empty($position)) {
|
||||
$whereMap[] = ['position', '=', $position];
|
||||
}
|
||||
|
||||
$list = $repo->slideList($whereMap, [], $page, $size, null, $orders);
|
||||
|
||||
return $this->json(0, 'success', $list);
|
||||
}
|
||||
|
||||
$this->data['positions'] = $repo->slidePositions();
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 排序
|
||||
* @return Json
|
||||
*/
|
||||
public function sort()
|
||||
{
|
||||
if (!$this->request->isPost()) {
|
||||
return $this->json(4000, '非法请求');
|
||||
}
|
||||
try {
|
||||
$id = $this->request->param('id/d', 0);
|
||||
$sort = $this->request->param('sort/d', 0);
|
||||
OperationRepository::getInstance()->updateSlide(['sort'=>$sort], $id);
|
||||
} catch (Exception $e) {
|
||||
return $this->json(4001, '排序失败');
|
||||
}
|
||||
return $this->json();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @return Json
|
||||
*/
|
||||
public function del()
|
||||
{
|
||||
if (!$this->request->isPost()) {
|
||||
return $this->json(4000, '非法请求');
|
||||
}
|
||||
|
||||
$ids = $this->request->param('ids/a', []);
|
||||
if (empty($ids)) {
|
||||
$ids[] = $this->request->param('id/d', 0);
|
||||
$ids = array_filter($ids);
|
||||
}
|
||||
|
||||
if (count($ids)) {
|
||||
OperationRepository::getInstance()->deleteSlides($ids);
|
||||
Log::write(get_class(), 'del', '删除了轮播图,涉及到的ID为:'.implode(',', $ids));
|
||||
}
|
||||
return $this->json();
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示位置下拉选项数据
|
||||
*
|
||||
* @param array $selected
|
||||
* @param array $disabled
|
||||
* @return false|string
|
||||
*/
|
||||
private function xmSelectPositionsJson(array $selected = [], array $disabled = [])
|
||||
{
|
||||
$positionList = OperationRepository::getInstance()->slidePositions();
|
||||
foreach ($positionList as $k => $item) {
|
||||
$positionList[$k]['selected'] = in_array($item['key'], $selected);
|
||||
$positionList[$k]['disabled'] = in_array($item['key'], $disabled);
|
||||
}
|
||||
$positionList = CmsRepository::getInstance()->handleSelectedList($positionList);
|
||||
return json_encode($positionList, JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 轮播图显示位置管理
|
||||
*
|
||||
*/
|
||||
public function position()
|
||||
{
|
||||
$repo = OperationRepository::getInstance();
|
||||
|
||||
if ($this->request->isPost()) {
|
||||
$list = $repo->slidePositionList([], [], 1, 0);
|
||||
|
||||
return $this->json(0, 'success', $list);
|
||||
}
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加显示位置信息
|
||||
*
|
||||
* @return Json|View
|
||||
*/
|
||||
public function addPosition()
|
||||
{
|
||||
$repo = OperationRepository::getInstance();
|
||||
|
||||
if ($this->request->isPost()) {
|
||||
$item = input('post.item/a');
|
||||
try {
|
||||
$this->validate($item, [
|
||||
'title|标题' => 'max:250',
|
||||
'key|位置标识' => 'require|max:100|alphaDash'
|
||||
]);
|
||||
} catch (ValidateException $e) {
|
||||
return $this->json(4002, $e->getError());
|
||||
}
|
||||
|
||||
if ($repo->slidePositionExists($item['key'])) {
|
||||
return $this->json(4003, '当前位置标识已存在!');
|
||||
}
|
||||
|
||||
$repo->createSlidePosition($item);
|
||||
return $this->json();
|
||||
}
|
||||
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑显示位置信息
|
||||
*
|
||||
* @return Json|View
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
* @throws DataNotFoundException
|
||||
*/
|
||||
public function editPosition()
|
||||
{
|
||||
$id = $this->request->param('id/d', 0);
|
||||
|
||||
if (!$position = OperationRepository::getInstance()->findSlidePositionById($id)) {
|
||||
return $this->json(4001, '数据不存在');
|
||||
}
|
||||
|
||||
if ($this->request->isPost()) {
|
||||
$item = input('post.item/a');
|
||||
try {
|
||||
$this->validate($item, [
|
||||
'title|标题' => 'max:250'
|
||||
]);
|
||||
} catch (ValidateException $e) {
|
||||
return $this->json(4002, $e->getError());
|
||||
}
|
||||
|
||||
unset($item['id']);
|
||||
unset($item['key']);
|
||||
OperationRepository::getInstance()->updateSlidePosition($item, $id);
|
||||
return $this->json();
|
||||
}
|
||||
|
||||
$this->data['item'] = $position;
|
||||
$this->data['id'] = $id;
|
||||
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除显示位置信息
|
||||
* @return Json
|
||||
*/
|
||||
public function delPosition()
|
||||
{
|
||||
if (!$this->request->isPost()) {
|
||||
return $this->json(4000, '非法请求');
|
||||
}
|
||||
|
||||
$ids = $this->request->param('ids/a', []);
|
||||
if (empty($ids)) {
|
||||
$ids[] = $this->request->param('id/d', 0);
|
||||
$ids = array_filter($ids);
|
||||
}
|
||||
|
||||
if (count($ids)) {
|
||||
OperationRepository::getInstance()->deleteSlidePositions($ids);
|
||||
Log::write(get_class(), 'delPosition', '删除了轮播显示位置,涉及到的ID为:'.implode(',', $ids));
|
||||
}
|
||||
|
||||
return $this->json();
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,239 @@
|
|||
<?php
|
||||
|
||||
namespace app\controller\manager;
|
||||
|
||||
use app\service\Image;
|
||||
use Intervention\Image\ImageManager;
|
||||
use Intervention\Image\ImageManagerStatic;
|
||||
use app\model\{System, File};
|
||||
use app\validate\Upload as VUpload;
|
||||
use think\facade\{Filesystem, Config, Lang};
|
||||
use think\Image as TImage;
|
||||
use app\controller\BaseController;
|
||||
|
||||
class Upload extends BaseController
|
||||
{
|
||||
protected $noNeedLogin = ['video', 'file', 'image', 'wangImage'];
|
||||
private $isCompress = true;
|
||||
private $validate;
|
||||
private $uploadPath;
|
||||
private $videoUploadPath;
|
||||
private $uploadPathIsWritable = 0;
|
||||
private $videoUploadPathIsWritable = 0;
|
||||
private $DIRECTORY_SEPARATOR = "/";
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$system = System::getSystem();
|
||||
if (!empty($system)) {
|
||||
$this->isCompress = $system['compress'] ?? true;
|
||||
}
|
||||
$this->validate = new VUpload();
|
||||
$this->uploadPath = Config::get('filesystem.disks.local.url');
|
||||
$this->videoUploadPath = Config::get('filesystem.disks.video.url');
|
||||
|
||||
$uploadDiskPath = public_path().$this->uploadPath;
|
||||
$videoUploadDiskPath = public_path().$this->videoUploadPath;
|
||||
if (!is_dir($uploadDiskPath)) {
|
||||
@mkdir($uploadDiskPath, 0777, true);
|
||||
}
|
||||
if (!is_dir($videoUploadDiskPath)) {
|
||||
@mkdir($videoUploadDiskPath, 0777, true);
|
||||
}
|
||||
if (is_writable($uploadDiskPath)) {
|
||||
$this->uploadPathIsWritable = 1;
|
||||
}
|
||||
if (is_writable($videoUploadDiskPath)) {
|
||||
$this->videoUploadPathIsWritable = 1;
|
||||
}
|
||||
|
||||
|
||||
$this->DIRECTORY_SEPARATOR = DIRECTORY_SEPARATOR == "\\" ? "/" : DIRECTORY_SEPARATOR;
|
||||
ini_set('max_execution_time', '0');
|
||||
ini_set("memory_limit", '-1');
|
||||
set_time_limit(0);
|
||||
}
|
||||
|
||||
//视频上传
|
||||
public function video()
|
||||
{
|
||||
$video = request()->file('video_video');
|
||||
if (!$this->videoUploadPathIsWritable) {
|
||||
return $this->json(1, '上传文件夹需要写入权限');
|
||||
}
|
||||
if ($this->validate->checkVideo($video)) {
|
||||
$md5 = $video->md5();//文件md5
|
||||
if ($fileItem = File::where('md5', $md5)->find()) {
|
||||
$return['src'] = $fileItem['src'];
|
||||
$fileItem['updated_at'] = date('Y-m-d H:i:s');
|
||||
$fileItem->save();
|
||||
return $this->json(200, '该文件已存在 路径为:'.$fileItem['path'], $return);
|
||||
}
|
||||
|
||||
$path = request()->param('path/s', '');//指定路径 基于public下 若为空则默认
|
||||
$hasPath = !empty($path);
|
||||
|
||||
// 去除以/storage/开头的部分 如/storage/20210808/test => 20210808/test
|
||||
$path = ltrim(trim($path, $this->DIRECTORY_SEPARATOR), \app\model\Attachment::ROOT_NAME.$this->DIRECTORY_SEPARATOR);
|
||||
$datePath = $hasPath ? $path : 'videos'.$this->DIRECTORY_SEPARATOR.date('Ym');//自定义目录
|
||||
checkPathExistWithMake(public_path().$this->uploadPath.'/'.$datePath);
|
||||
$src = Filesystem::putFile($datePath, $video, 'uniqid');
|
||||
$src = $this->uploadPath.'/'.$src;
|
||||
$return['src'] = $src;
|
||||
File::add($video, $src, $md5, 'video'); //加入上传文件表
|
||||
return $this->json(0, 'ok', $return);
|
||||
} else {
|
||||
|
||||
$errorMsg = Lang::get($this->validate->getError());
|
||||
return $this->json(1, $errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
//文件上传(通用)
|
||||
public function file()
|
||||
{
|
||||
$file = request()->file('file_file');
|
||||
$md5 = $file->md5();//文件md5
|
||||
$fileName = $file->getOriginalName();//原始文件名
|
||||
if ($fileItem = File::where('md5', $md5)->find()) {
|
||||
$return['src'] = $fileItem['src'];
|
||||
$return['name'] = $fileName;
|
||||
$fileItem['updated_at'] = date('Y-m-d H:i:s');
|
||||
return $this->json(200, '该文件已存在 路径为:'.$fileItem['path'], $return);
|
||||
}
|
||||
if ($this->validate->checkFile($file)) {
|
||||
try {
|
||||
if (!$this->uploadPathIsWritable) {
|
||||
throw new \Exception('上传文件夹需要写入权限');
|
||||
}
|
||||
$path = request()->param('path/s', '');//指定路径 基于public下 若为空则默认
|
||||
$hasPath = !empty($path);
|
||||
$datePath = $hasPath ? $path : 'files'.$this->DIRECTORY_SEPARATOR.date('Ym');//自定义目录
|
||||
checkPathExistWithMake(public_path().$this->uploadPath.'/'.$datePath);
|
||||
$src = Filesystem::putFile('files'.$this->DIRECTORY_SEPARATOR.date('Ym'), $file, 'uniqid');
|
||||
$src = $this->uploadPath.$this->DIRECTORY_SEPARATOR.$src;
|
||||
$return['src'] = $src;
|
||||
$return['name'] = $fileName;
|
||||
File::add($file, $src, $md5, 'file'); //加入上传文件表
|
||||
} catch (\Exception $e) {
|
||||
return $this->json(1, $e->getMessage());
|
||||
}
|
||||
return $this->json(0, 'ok', $return);
|
||||
} else {
|
||||
$errorMsg = Lang::get($this->validate->getError());
|
||||
return $this->json(1, $errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
//图片上传(通用)
|
||||
public function image()
|
||||
{
|
||||
// 字段名 image-image避免冲突 layui组件自动生成的隐藏file input框中name容易重名冲突
|
||||
$image = request()->file('image_image');
|
||||
|
||||
// $img = ImageManagerStatic::cache(function($imageObj) use ($image) {
|
||||
// $imageObj->make($image)->resize(300, 200)->greyscale();
|
||||
// }, 10, true);
|
||||
// $img = (string)ImageManagerStatic::make($image)->fit(350, 610)->encode('png');
|
||||
// echo $img;
|
||||
// exit;
|
||||
|
||||
$md5 = $image->md5();//文件md5
|
||||
$path = request()->param('path/s', '');//指定路径 基于public下 若为空则默认
|
||||
$hasPath = !empty($path);
|
||||
|
||||
if ($this->validate->checkImage($image)) {
|
||||
if ($fileItem = File::where('md5', $md5)->find()) {
|
||||
$return['src'] = $fileItem['src'];
|
||||
$fileItem['updated_at'] = date('Y-m-d H:i:s');
|
||||
|
||||
return $this->json(200, '该文件已存在 路径为:'.$fileItem['path'], $return);
|
||||
}
|
||||
|
||||
try {
|
||||
if (!$this->uploadPathIsWritable) {
|
||||
throw new \Exception('上传文件夹需要写入权限');
|
||||
}
|
||||
|
||||
// 去除以/storage/开头的部分 如/storage/20210808/test => 20210808/test
|
||||
if (strpos(trim($path, $this->DIRECTORY_SEPARATOR), \app\model\Attachment::ROOT_NAME.$this->DIRECTORY_SEPARATOR) === 0) {
|
||||
$path = substr(trim($path, $this->DIRECTORY_SEPARATOR), strlen(\app\model\Attachment::ROOT_NAME.$this->DIRECTORY_SEPARATOR));
|
||||
}
|
||||
|
||||
$datePath = $hasPath ? $path : 'images'.$this->DIRECTORY_SEPARATOR.date('Ym');//自定义目录
|
||||
$datePath = ($datePath == $this->DIRECTORY_SEPARATOR."storage") ? $this->DIRECTORY_SEPARATOR : $datePath;
|
||||
|
||||
checkPathExistWithMake(public_path().$this->uploadPath.'/'.$datePath);
|
||||
|
||||
$src = Filesystem::putFile($datePath, $image, 'uniqid');
|
||||
|
||||
$src = $this->uploadPath.$this->DIRECTORY_SEPARATOR.$src;
|
||||
$suffix = strtolower($image->getOriginalExtension());
|
||||
|
||||
$cutJson= input('cut');
|
||||
$cutList = json_decode($cutJson, true);
|
||||
// 裁剪尺寸
|
||||
if (!empty($cutList)) {
|
||||
$srcArr = explode('.', $src);
|
||||
foreach ($cutList as $cut) {
|
||||
ImageManagerStatic::make($image)->fit($cut['width'], $cut['height'])->save(public_path().$srcArr[0].'_'.$cut['name'].'.'.$suffix);
|
||||
}
|
||||
}
|
||||
$return['src'] = $src;
|
||||
File::add($image, $src, $md5); //加入上传文件表
|
||||
} catch (\Exception $e) {
|
||||
return $this->json(1, $e->getMessage());
|
||||
}
|
||||
return $this->json(0, 'ok', $return);
|
||||
} else {
|
||||
$errorMsg = Lang::get($this->validate->getError());
|
||||
return $this->json(1, $errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
//富文本编辑器商城图片
|
||||
public function wangImage()
|
||||
{
|
||||
|
||||
$imageArr = request()->file('wang_img'); // 该方式,前端js上传方法中字段名称必须以数组形式传参 如 wang_img[] = 值
|
||||
$errno = 0;
|
||||
$data = [];
|
||||
|
||||
if (!$this->uploadPathIsWritable) {
|
||||
$errno = 1;
|
||||
$data[] = '上传文件夹需要写入权限';
|
||||
} else {
|
||||
foreach ($imageArr as $image) {
|
||||
$md5 = $image->md5();//文件md5
|
||||
if ($fileItem = File::where('md5', $md5)->find()) {
|
||||
$return['src'] = $fileItem['src'];
|
||||
$fileItem['updated_at'] = date('Y-m-d H:i:s');
|
||||
|
||||
$data[] = $fileItem['src'];
|
||||
$fileItem->save();
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($this->validate->checkImage($image)) {
|
||||
checkPathExistWithMake(public_path().$this->uploadPath.'/images/'.date('Ym'));
|
||||
$src = Filesystem::putFile('images/'.date('Ym'), $image, 'uniqid');
|
||||
$src = $this->uploadPath.$this->DIRECTORY_SEPARATOR.$src;
|
||||
$data[] = $src;
|
||||
if ($this->isCompress) {
|
||||
Image::resize($src);
|
||||
}
|
||||
File::add($image, $src, $md5); //加入上传文件表
|
||||
} else {
|
||||
$errno = 1;
|
||||
$data = [];
|
||||
$data[] = Lang::get($this->validate->getError());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$return['errno'] = $errno;
|
||||
$return['data'] = $data;
|
||||
return json($return);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace app\controller\manager;
|
||||
|
||||
|
||||
/**
|
||||
* 微信公众号配置
|
||||
* Class Wechat
|
||||
* @package app\controller\manager
|
||||
*/
|
||||
class Wechat extends Base
|
||||
{
|
||||
// 公众号设置
|
||||
public function base()
|
||||
{
|
||||
// TODO
|
||||
return 'TODO ...';
|
||||
}
|
||||
|
||||
// 公众号设置
|
||||
public function menu()
|
||||
{
|
||||
// TODO
|
||||
return 'TODO ...';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace app\controller\manager;
|
||||
|
||||
|
||||
|
||||
class map extends Base
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* 商家列表列表
|
||||
*
|
||||
* @return Json|View
|
||||
* @throws Exception
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
|
||||
return $this->view();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
// 事件定义文件
|
||||
return [
|
||||
'bind' => [
|
||||
],
|
||||
|
||||
'listen' => [
|
||||
'AppInit' => [],
|
||||
'HttpRun' => [],
|
||||
'HttpEnd' => [],
|
||||
'LogLevel' => [],
|
||||
'LogWrite' => [],
|
||||
],
|
||||
|
||||
'subscribe' => [],
|
||||
];
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace app\exception;
|
||||
|
||||
use Exception;
|
||||
|
||||
class ApiRedirectException extends Exception
|
||||
{
|
||||
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace app\exception;
|
||||
|
||||
class RepositoryException extends \Exception
|
||||
{
|
||||
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
namespace app\exception;
|
||||
|
||||
class TraitException extends \Exception
|
||||
{
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
namespace app\job;
|
||||
|
||||
use app\repository\AccountRepository;
|
||||
use think\queue\Job;
|
||||
|
||||
class NotifySms
|
||||
{
|
||||
public function fire(Job $job, $data){
|
||||
|
||||
//短信通知列表
|
||||
if ($data) {
|
||||
foreach ($data as $item) {
|
||||
echo sprintf("短信发送成功!phone:%s time:%s \n", $item['mobile'], date('Y-m-d H:i:s'));
|
||||
sleep(mt_rand(1,3));
|
||||
}
|
||||
}
|
||||
|
||||
// if ($job->attempts() > 3) {
|
||||
// //通过这个方法可以检查这个任务已经重试了几次了
|
||||
// echo sprintf('发送短信失败过多');
|
||||
// }
|
||||
|
||||
|
||||
//如果任务执行成功后 记得删除任务,不然这个任务会重复执行,直到达到最大重试次数后失败后,执行failed方法
|
||||
$job->delete();
|
||||
|
||||
// 也可以重新发布这个任务
|
||||
// $job->release(3); //$delay为延迟时间
|
||||
|
||||
}
|
||||
|
||||
public function failed($data){
|
||||
|
||||
// ...任务达到最大重试次数后,失败了
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
// 全局中间件定义文件
|
||||
return [
|
||||
// 全局请求缓存
|
||||
// \think\middleware\CheckRequestCache::class,
|
||||
// 多语言加载
|
||||
//\think\middleware\LoadLangPack::class,
|
||||
// Session初始化
|
||||
\think\middleware\SessionInit::class,
|
||||
];
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
namespace app\middleware;
|
||||
|
||||
use Closure;
|
||||
use app\service\Jwt as JwtService;
|
||||
|
||||
/**
|
||||
* API登录认证(需要先调用JWT解析用户信息)
|
||||
* Class ApiLogin
|
||||
* @package app\middleware
|
||||
*/
|
||||
class ApiLogin
|
||||
{
|
||||
public function handle($request, Closure $next) {
|
||||
$authorization = $request->authorization ?? '';
|
||||
if (empty($authorization)) {
|
||||
return json(['code' => 6001, 'msg' => '请填写token']);
|
||||
}
|
||||
if (!JwtService::validate($authorization)) {
|
||||
return json(['code' => 6001, 'msg' => 'token验证失败或已失效']);
|
||||
}
|
||||
|
||||
$userInfo = $request->user ?? [];
|
||||
if (!isset($userInfo['user_id']) || empty($userInfo['user_id'])) {
|
||||
return json(['code' => 6001, 'msg' => 'token已失效']);
|
||||
}
|
||||
|
||||
// 自定义过期时间校验。
|
||||
if(isset($userInfo['expire_time']) && time() >= $userInfo['expire_time']) {
|
||||
return json(['code' => 6001, 'msg' => 'token已失效']);
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
namespace app\middleware;
|
||||
|
||||
use Closure;
|
||||
use app\model\AuthRule;
|
||||
use tauthz\facade\Enforcer;
|
||||
use think\facade\Cache;
|
||||
|
||||
class Auth
|
||||
{
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
$auth = session('auth');
|
||||
if (!$auth) {
|
||||
return redirect(url('manager.login/index'));
|
||||
}
|
||||
|
||||
$module = 'manager';
|
||||
$controller = unCamelize(request()->controller());
|
||||
$controller = str_replace($module.'.', '', $controller);
|
||||
$controller = str_replace('.', '/', $controller);//兼容多层级目录 如 /manager/test/article/index
|
||||
$action = unCamelize(request()->action());
|
||||
$roles = Enforcer::getRolesForUser($auth['user_id']);
|
||||
// $per = Enforcer::getPermissionsForUser($roles[0]);
|
||||
// var_dump($controller);
|
||||
// var_dump($action);
|
||||
// var_dump($roles);
|
||||
// var_dump($per);
|
||||
// exit;
|
||||
// return $next($request);//暂时停用权限校验
|
||||
// var_dump($controller);
|
||||
// var_dump($action);
|
||||
// var_dump(Enforcer::hasPermissionForUser(1, $controller, 'group-make'));exit;
|
||||
|
||||
foreach ($roles as $role) {
|
||||
// TODO 关注批量权限检测是否可用
|
||||
//只需要有一个角色具有权限就放通 此处第一个参数不是用户 而是 角色 此方法是检测用户|角色是否具有某个权限的公用方法
|
||||
if (Enforcer::hasPermissionForUser($role, $controller, $action)) {
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
|
||||
if (request()->isAjax()) {
|
||||
return json(['code' => 4001, 'msg' => '没有权限']);
|
||||
} else {
|
||||
return view('/manager/error/jump')->assign('msg', '很抱歉,您还没有权限,请联系管理员开通!');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
namespace app\middleware;
|
||||
|
||||
use Closure;
|
||||
use think\Request;
|
||||
|
||||
/**
|
||||
* CSRF校验
|
||||
*/
|
||||
class Csrf
|
||||
{
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
if($request->isPost()){
|
||||
$check = $request->checkToken();
|
||||
if(false === $check) {
|
||||
// return $this->csrfError($request);
|
||||
}
|
||||
}
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
protected function csrfError($request, $msg = '非法请求, 用户身份认证失败!')
|
||||
{
|
||||
if($request->isAjax()) {
|
||||
return json(['code' => 401, 'msg' => $msg], 200);
|
||||
} else {
|
||||
$referer = $_SERVER['HTTP_REFERER'] ?? null;
|
||||
if (empty($referer)) {
|
||||
$url = '/';
|
||||
} else {
|
||||
$domain = $request->domain();
|
||||
$urlInfo = parse_url($referer);
|
||||
$scheme = $urlInfo['scheme'] ?? '';
|
||||
$requestSrc = '';
|
||||
if (!empty($scheme)) {
|
||||
$requestSrc = $scheme.'://'.($urlInfo['host'] ?? '');
|
||||
}
|
||||
if($domain != $requestSrc) {
|
||||
$url = '/';
|
||||
} else {
|
||||
$url = 'javascript:history.back(-1);';
|
||||
}
|
||||
}
|
||||
$errorData = [
|
||||
'code'=> 401,
|
||||
'msg' => $msg,
|
||||
'data' => [],
|
||||
'wait' => 5,
|
||||
'url' => $url
|
||||
];
|
||||
return view('error/400', $errorData);
|
||||
// 返回401视图 response type has html、json、jsonp、xml、file、view、redirect
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
namespace app\middleware;
|
||||
|
||||
use Closure;
|
||||
use app\service\Jwt as JwtService;
|
||||
|
||||
/**
|
||||
* 根据TOKEN解析用户信息
|
||||
*
|
||||
* Class JWT
|
||||
* @package app\middleware
|
||||
*/
|
||||
class JWT
|
||||
{
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
$authorization = $request->header('Authorization');
|
||||
$tokenStr = $request->param('token/s', '');
|
||||
|
||||
if ($authorization) {
|
||||
$authorization = str_replace('Bearer ', '', $authorization);
|
||||
}
|
||||
|
||||
//优先取header中token
|
||||
$token = $authorization ?: $tokenStr;
|
||||
$userInfo = [];
|
||||
if (!empty($token)) {
|
||||
$userInfo = JwtService::parse($token);//token中携带的简易用户信息
|
||||
}
|
||||
|
||||
$request->user = $userInfo;
|
||||
// authorization用于移交ApiLogin认证
|
||||
$request->authorization = $token;
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace app\middleware;
|
||||
|
||||
use Closure;
|
||||
use think\Request;
|
||||
|
||||
/**
|
||||
* 日志记录
|
||||
*
|
||||
* Class Log
|
||||
* @package app\middleware
|
||||
*/
|
||||
class Log
|
||||
{
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
$response = $next($request);
|
||||
|
||||
// 添加中间件执行代码
|
||||
\app\model\Log::write($request->controller(), $request->action(), $request->pathinfo(), $request->method());
|
||||
|
||||
return $response;
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace app\middleware;
|
||||
|
||||
use Closure;
|
||||
use think\Request;
|
||||
|
||||
/**
|
||||
* 前台登录验证
|
||||
*
|
||||
* Class Login
|
||||
* @package app\middleware
|
||||
*/
|
||||
class Login
|
||||
{
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
if (!session('frontend_auth')) {
|
||||
$url = $request->url(true);
|
||||
return redirect(url('login/index').'?url='.urlencode($url));
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace app\model;
|
||||
|
||||
use think\model;
|
||||
use think\model\relation\BelongsToMany;
|
||||
use think\model\relation\HasOne;
|
||||
|
||||
class Account extends Base
|
||||
{
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
|
||||
namespace app\model;
|
||||
|
||||
use app\exception\RepositoryException;
|
||||
use think\db\exception\DataNotFoundException;
|
||||
use think\db\exception\DbException;
|
||||
use think\db\exception\ModelNotFoundException;
|
||||
use think\model\relation\HasMany;
|
||||
use think\model\relation\HasOne;
|
||||
|
||||
class Archives extends Base
|
||||
{
|
||||
public const ORIGINAL_TABLE = 'bee_archives';
|
||||
|
||||
public const STATUS_NORMAL = 1;//正常
|
||||
public const STATUS_DISABLE = 0;//禁用
|
||||
|
||||
//根据栏目ID获取文章分页列表
|
||||
public static function getListPageByCategory($categoryId, $per = 20, $keyword = '')
|
||||
{
|
||||
$where = [
|
||||
['category_id', '=', $categoryId],
|
||||
];
|
||||
$param = [];
|
||||
//$param['category_id'] = $categoryId;
|
||||
if ($keyword != '') {
|
||||
$where[] = ['title', 'like', '%' . $keyword . '%'];
|
||||
$param['keyword'] = $keyword;
|
||||
}
|
||||
$paginate = [
|
||||
'list_rows' => $per,
|
||||
'query' => $param
|
||||
];
|
||||
return self::with(["archivesCategory"])->where($where)
|
||||
->order(["sort"=>"desc"])
|
||||
->paginate($paginate, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 创建人信息
|
||||
*
|
||||
* @return HasOne
|
||||
*/
|
||||
public function member(): HasOne
|
||||
{
|
||||
return $this->hasOne(Member::class, 'id', 'created_by')->bind(['nickname']);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 栏目 后台用
|
||||
*
|
||||
* @return HasOne
|
||||
*/
|
||||
public function category(): HasOne
|
||||
{
|
||||
return $this->hasOne(ArchivesCategory::class, 'id', 'category_id')->bind(['category' => 'title']);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 栏目
|
||||
* @return HasOne
|
||||
*/
|
||||
public function archivesCategory(): HasOne
|
||||
{
|
||||
return $this->hasOne(ArchivesCategory::class, 'id', 'category_id');
|
||||
}
|
||||
|
||||
|
||||
public static function onAfterInsert( $archives)
|
||||
{
|
||||
$archives->sort = ceil(self::max("sort")+1);
|
||||
$archives->save();
|
||||
}
|
||||
|
||||
public static function onBeforeUpdate( $archives)
|
||||
{
|
||||
//检查sort
|
||||
$hasFlag = self::where([
|
||||
["sort","=",$archives->sort],
|
||||
["id","<>",$archives->id]
|
||||
])->find();
|
||||
if(!empty($hasFlag)){
|
||||
throw new RepositoryException('sort不能重复,保留小数点后2位');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
<?php
|
||||
|
||||
namespace app\model;
|
||||
|
||||
use think\Collection;
|
||||
use think\db\exception\DbException;
|
||||
use think\db\exception\DataNotFoundException;
|
||||
use think\db\exception\ModelNotFoundException;
|
||||
use think\model\relation\HasOne;
|
||||
|
||||
class ArchivesCategory extends Base
|
||||
{
|
||||
const index_id = 1;//首页
|
||||
/**
|
||||
* 检测数据下 是否有子栏目
|
||||
*
|
||||
* @param array $ids
|
||||
* @return bool
|
||||
*/
|
||||
public static function hasChildrenByIds(array $ids): bool
|
||||
{
|
||||
return self::whereIn('pid', $ids)->count() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测数据下 是否有子内容
|
||||
*
|
||||
* @param array $ids
|
||||
* @return bool
|
||||
*/
|
||||
public static function hasContentByIds(array $ids): bool
|
||||
{
|
||||
return Archives::whereIn('category_id', $ids)->count() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取列表
|
||||
*
|
||||
* @return ArchivesCategory[]|array|Collection
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
*/
|
||||
public static function getList()
|
||||
{
|
||||
return self::field('id,pid,title,name,sort,path,true as open')
|
||||
->order('sort', 'desc')
|
||||
->order('id', 'asc')
|
||||
->select();
|
||||
}
|
||||
|
||||
/**
|
||||
* 模型
|
||||
*
|
||||
* @return HasOne
|
||||
*/
|
||||
public function model(): HasOne
|
||||
{
|
||||
return $this->hasOne(ArchivesModel::class, 'id', 'model_id')->bind(['model' => 'name']);
|
||||
}
|
||||
|
||||
|
||||
//--王兴龙 auth start --------------------------------------------------------------------------------------------------
|
||||
//获取前台菜单
|
||||
public static function getListForFrontMenu()
|
||||
{
|
||||
$items = self::withJoin(["model"])
|
||||
->order(['sort' =>'desc',"id"=>"asc"])
|
||||
->select()
|
||||
->toArray();
|
||||
return self::getCates($items);
|
||||
}
|
||||
|
||||
//获取递归栏目
|
||||
public static function getCates($cates, $parentId = 0)
|
||||
{
|
||||
$menus = [];
|
||||
foreach ($cates as $cate) {
|
||||
if ($cate['pid'] == $parentId) {
|
||||
$children = self::getCates($cates, $cate['id']);
|
||||
if (!empty($children)) {
|
||||
$cate['children'] = $children;
|
||||
}
|
||||
$menus[] = $cate;
|
||||
}
|
||||
}
|
||||
return $menus;
|
||||
}
|
||||
|
||||
//当前分类的最高级分类Id
|
||||
public static function firstGradeById($id)
|
||||
{
|
||||
$res = 0;
|
||||
$item = self::getById($id);
|
||||
if ($item) {
|
||||
$res = $id;
|
||||
if ($item['pid'] > 0) {
|
||||
$items = self::select()->toArray();
|
||||
$first = self::getFirstGrade($items, $item['pid']);
|
||||
if (!empty($first)) {
|
||||
$res = $first['id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
public static function getFirstGrade($items, $parentId)
|
||||
{
|
||||
$data = [];
|
||||
foreach ($items as $key => $item) {
|
||||
if ($item['id'] == $parentId) {
|
||||
if ($item['pid'] > 0) {
|
||||
unset($items[$key]);
|
||||
$parent = self::getFirstGrade($items, $item['pid']);
|
||||
if (!empty($parent)) {
|
||||
$data = $parent;
|
||||
} else {
|
||||
$data = $item;
|
||||
}
|
||||
} else {
|
||||
$data = $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取每个栏目文章封面大小 数组
|
||||
* */
|
||||
public static function getArchiveCoverSizeArr()
|
||||
{
|
||||
return json_encode(self::column(["id","archive_cover_size"],"id"));
|
||||
}
|
||||
/**
|
||||
* 获取每个栏目 设置路由
|
||||
* */
|
||||
public static function getRoute()
|
||||
{
|
||||
return self::column(["id","name","route"],"id");
|
||||
}
|
||||
|
||||
//--王兴龙 auth end --------------------------------------------------------------------------------------------------
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace app\model;
|
||||
|
||||
/**
|
||||
* 文档模型
|
||||
* Class ArchivesModel
|
||||
* @package app\model
|
||||
*/
|
||||
class ArchivesModel extends Base
|
||||
{
|
||||
public const MODEL_ARCHIVES = 'archives'; // 文章
|
||||
static function allModel()
|
||||
{
|
||||
return [
|
||||
self::MODEL_ARCHIVES=>"1",
|
||||
];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
<?php
|
||||
|
||||
namespace app\model;
|
||||
|
||||
use Exception;
|
||||
use think\db\exception\DataNotFoundException;
|
||||
use think\db\exception\DbException;
|
||||
use think\db\exception\ModelNotFoundException;
|
||||
use think\facade\Db;
|
||||
|
||||
/**
|
||||
* 文档模型字段
|
||||
* Class ArchivesModelField
|
||||
* @package app\model
|
||||
*/
|
||||
class ArchivesModelField extends Base
|
||||
{
|
||||
public const VIRTUAL_YES = 1;//虚拟字段 是
|
||||
public const VIRTUAL_NO = 0;//虚拟字段 否
|
||||
|
||||
/**
|
||||
* 模型添加字段列表
|
||||
*
|
||||
* @param int $modelId
|
||||
* @param string $modelName
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function setFieldList(int $modelId, string $modelName)
|
||||
{
|
||||
if (self::where('model_id', $modelId)->count() <= 0) {
|
||||
$rs = Db::query("SHOW FULL COLUMNS FROM ".Archives::ORIGINAL_TABLE);
|
||||
$insert = [];
|
||||
foreach ($rs as $val) {
|
||||
$arr = [];
|
||||
$arr['model_id'] = $modelId;
|
||||
$arr['model'] = $modelName;
|
||||
$arr['name'] = $val['Field'];
|
||||
$arr['title'] = $val['Comment'];
|
||||
$arr['remark'] = $val['Comment'];
|
||||
$insert[] = $arr;
|
||||
}
|
||||
|
||||
(new self())->saveAll($insert);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步字段
|
||||
*
|
||||
* @param int $modelId
|
||||
* @param string $modelName
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
*/
|
||||
public static function syncFieldList(int $modelId, string $modelName)
|
||||
{
|
||||
$rs = Db::query("SHOW FULL COLUMNS FROM ".Archives::ORIGINAL_TABLE);
|
||||
$oldFieldList = self::where('model_id', $modelId)->where('is_virtual', self::VIRTUAL_NO)->select();
|
||||
$oldFields = $oldFieldList->column('name');
|
||||
|
||||
|
||||
$newestFields = [];
|
||||
foreach ($rs as $val) {
|
||||
$newestFields[] = $val['Field'];
|
||||
}
|
||||
|
||||
//待删除字段
|
||||
$delete = array_diff($oldFields, $newestFields);
|
||||
|
||||
//待新增字段
|
||||
$needInsertFields = array_diff($newestFields, $oldFields);
|
||||
|
||||
$insert = [];//新增字段
|
||||
foreach ($rs as $val) {
|
||||
if (in_array($val['Field'], $needInsertFields)) {
|
||||
$arr = [];
|
||||
$arr['model_id'] = $modelId;
|
||||
$arr['model'] = $modelName;
|
||||
$arr['name'] = $val['Field'];
|
||||
$arr['title'] = $val['Comment'];
|
||||
$arr['remark'] = $val['Comment'];
|
||||
$insert[] = $arr;
|
||||
}
|
||||
}
|
||||
|
||||
(new self())->saveAll($insert);
|
||||
(new self())->whereIn('name', $delete)->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* 各栏目[模型] 可展示字段列表
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function showFieldList(): array
|
||||
{
|
||||
$list = self::alias('amf')
|
||||
->leftJoin('archives_category ac', 'ac.model_id = amf.model_id')
|
||||
->field('amf.*, ac.id as category_id, ac.title as category_title')
|
||||
->where('amf.status', self::COMMON_ON)
|
||||
->where('ac.id', '>', 0)
|
||||
->select();
|
||||
|
||||
$res = [];
|
||||
|
||||
$list = $list->toArray();
|
||||
foreach ($list as $item) {
|
||||
if (!isset($res[$item['category_id']])) {
|
||||
$res[$item['category_id']] = [];
|
||||
}
|
||||
|
||||
$res[$item['category_id']][] = $item['name'];
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
namespace app\model;
|
||||
|
||||
use Exception;
|
||||
use think\exception\ValidateException;
|
||||
|
||||
class Attachment extends Base
|
||||
{
|
||||
protected $name = 'file';
|
||||
|
||||
public const TYPE_DIR = 'dir';//目录
|
||||
public const TYPE_IMG = 'image';//图片
|
||||
public const TYPE_VIDEO = 'video';//视频
|
||||
public const TYPE_FILE = 'file';//文件
|
||||
|
||||
public const ROOT_NAME = 'storage';//根目录名称
|
||||
public const ROOT_PATH = "/".self::ROOT_NAME;//文件存放根路径
|
||||
|
||||
public static function list(): array
|
||||
{
|
||||
$where[] = ['src', 'like', '%'.self::ROOT_PATH."/"];
|
||||
return self::findList($where);
|
||||
}
|
||||
|
||||
/**
|
||||
* 路径目录处理 会逐级检查路径上所有目录是否存在 不存在的目录全部创建[创建到数据库]
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function pathDirHandle(string $path): bool
|
||||
{
|
||||
$pathInfo = pathinfo($path);
|
||||
$fullPath = isset($pathInfo['extension']) ? $pathInfo['dirname'] : $pathInfo['dirname']."/".$pathInfo['filename'];
|
||||
|
||||
// 全路径 如 /storage/dir1/dir2/dir3/dir4/ 注意前后都有/
|
||||
$fullPath = $fullPath."/";
|
||||
|
||||
$pathArr = explode("/", trim($fullPath, "/"));
|
||||
|
||||
$insert = [];
|
||||
$now = date('Y-m-d H:i:s');
|
||||
try {
|
||||
// 检测全路径上所有目录是否存在 不存在则创建
|
||||
foreach ($pathArr as $k => $p) {
|
||||
$currentPath = '/';
|
||||
$currentArr = array_slice($pathArr, 0, $k);
|
||||
if ($currentArr) {
|
||||
$currentPath = "/".implode('/', $currentArr)."/";
|
||||
}
|
||||
if ($currentPath != '/' && self::where('path', $currentPath)->where('name', $p)->count() <= 0) {
|
||||
$arr = [];
|
||||
$arr['path'] = $currentPath;
|
||||
$arr['name'] = $p;
|
||||
$arr['created_at'] = $now;
|
||||
$arr['is_dir'] = self::COMMON_ON;
|
||||
$arr['type'] = self::TYPE_DIR;
|
||||
|
||||
$insert[] = $arr;
|
||||
}
|
||||
}
|
||||
|
||||
(new self())->saveAll($insert);
|
||||
return true;
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,390 @@
|
|||
<?php
|
||||
|
||||
namespace app\model;
|
||||
|
||||
use Exception;
|
||||
use think\Collection;
|
||||
use think\db\exception\DataNotFoundException;
|
||||
use think\db\exception\DbException;
|
||||
use think\db\exception\ModelNotFoundException;
|
||||
use think\Model;
|
||||
use think\Paginator;
|
||||
|
||||
class Base extends Model
|
||||
{
|
||||
protected $autoWriteTimestamp = false;
|
||||
|
||||
// 布尔值数字关系
|
||||
public const BOOL_FALSE = 0;
|
||||
public const BOOL_TRUE = 1;
|
||||
|
||||
public const COMMON_ON = 1;
|
||||
public const COMMON_OFF = 0;
|
||||
|
||||
//根据Id列表获取列表
|
||||
public static function getListByIds($ids)
|
||||
{
|
||||
if (count($ids) == 0 || empty($ids)) {
|
||||
return [];
|
||||
}
|
||||
return self::where('id', 'in', $ids)->select()->toArray();
|
||||
}
|
||||
|
||||
//根据ID获取单条数据
|
||||
public static function getById($id)
|
||||
{
|
||||
if ($id <= 0) {
|
||||
return [];
|
||||
}
|
||||
return self::where('id', $id)->findOrEmpty()->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过ID查找记录
|
||||
*
|
||||
* @param int $id
|
||||
* @param array $fields
|
||||
* @param callable|null $call
|
||||
* @return array|Model|null
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
*/
|
||||
public static function findById(int $id, array $fields = [], callable $call = null)
|
||||
{
|
||||
$q = self::when(!empty($fields), function ($q) use ($fields) {
|
||||
$q->field($fields);
|
||||
});
|
||||
if ($call !== null) {
|
||||
$q = $call($q);
|
||||
}
|
||||
return $q->find($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找单个记录
|
||||
*
|
||||
* @param array $where
|
||||
* @param array $fields
|
||||
* @param callable|null $call
|
||||
* @return array|Model|null
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
*/
|
||||
public static function findOne(array $where = [], array $fields = [], callable $call = null)
|
||||
{
|
||||
$q = self::when(!empty($fields), function ($q) use ($fields) {
|
||||
$q->field($fields);
|
||||
})->where($where);
|
||||
|
||||
if ($call !== null) {
|
||||
$q = $call($q);
|
||||
}
|
||||
return $q->find();
|
||||
}
|
||||
|
||||
//根据ID更新数据
|
||||
public static function updateById($id, $data)
|
||||
{
|
||||
return self::where('id', $id)->update($data);
|
||||
}
|
||||
|
||||
//根据where条件和排序获取记录
|
||||
public static function getListByWhereAndOrder($where, $order, $limit = 1)
|
||||
{
|
||||
return self::where($where)
|
||||
->order($order)
|
||||
->limit($limit)
|
||||
->select()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID删除数据
|
||||
*
|
||||
* @param int $id
|
||||
* @return bool
|
||||
*/
|
||||
public static function deleteById(int $id): bool
|
||||
{
|
||||
return self::where('id', $id)->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID列表删除数据
|
||||
*
|
||||
* @param array $ids
|
||||
* @return bool
|
||||
*/
|
||||
public static function deleteByIds(array $ids): bool
|
||||
{
|
||||
return self::whereIn('id', $ids)->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* 排序
|
||||
*
|
||||
* @param int $id 调整ID
|
||||
* @param string $type 本次操作类型 向上、向下
|
||||
* @param int $num 移动位数
|
||||
* @param string $listType 列表的排序方式 默认为降序
|
||||
* @param array $where 额外条件 如限制在指定分类下 where[] = ['category_id', '=', 6]
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function sort(int $id, string $type, int $num = 1, string $listType = 'desc', array $where = []): array
|
||||
{
|
||||
$res = ['code' => 0, 'msg' => 'success'];
|
||||
$item = self::getById($id);
|
||||
|
||||
if (!$item) {
|
||||
$res['code'] = 1;
|
||||
$res['msg'] = '记录不存在';
|
||||
return $res;
|
||||
}
|
||||
|
||||
if ($listType == 'desc') {
|
||||
if ($type == 'down') {
|
||||
$where[] = ['sort', '<', $item['sort']];
|
||||
$order = "sort desc";
|
||||
} else {
|
||||
$where[] = ['sort', '>', $item['sort']];
|
||||
$order = "sort asc";
|
||||
}
|
||||
} else {
|
||||
if ($type == 'up') {
|
||||
$where[] = ['sort', '<', $item['sort']];
|
||||
$order = "sort desc";
|
||||
} else {
|
||||
$where[] = ['sort', '>', $item['sort']];
|
||||
$order = "sort asc";
|
||||
}
|
||||
}
|
||||
|
||||
$forSortItems = self::getListByWhereAndOrder($where, $order, $num);
|
||||
if (!empty($forSortItems)) {
|
||||
$updateData = [];
|
||||
$forSortCount = count($forSortItems);
|
||||
for ($i = 0; $i < $forSortCount; $i++) {
|
||||
if ($i == 0) {
|
||||
$updateData[] = [
|
||||
'id' => $forSortItems[$i]['id'],
|
||||
'sort' => $item['sort']
|
||||
];
|
||||
} else {
|
||||
$updateData[] = [
|
||||
'id' => $forSortItems[$i]['id'],
|
||||
'sort' => $forSortItems[$i - 1]['sort']
|
||||
];
|
||||
}
|
||||
}
|
||||
$updateData[] = [
|
||||
'id' => $item['id'],
|
||||
'sort' => $forSortItems[$i - 1]['sort']
|
||||
];
|
||||
|
||||
if (!empty($updateData)) {
|
||||
$obj = new static();
|
||||
$obj->saveAll($updateData);
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
$res['code'] = 1;
|
||||
$res['msg'] = '无需调整';
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询列表 [带分页 适用于后台]
|
||||
*
|
||||
* @param array $data 查询数据 格式如下
|
||||
* [
|
||||
* 'fields' => ['id','title','desc'],//查询字段
|
||||
* 'where' => [
|
||||
* ['name', 'like', '%thinkphp%'],
|
||||
* ['title', 'like', '%thinkphp%'],
|
||||
* ['id', '>', 0],
|
||||
* ['status', '=', 1],
|
||||
* ],//查询条件
|
||||
* 'order' => ['order'=>'desc','id'=>'desc'],//排序
|
||||
* 'size' => 50,//每页数量
|
||||
* ]
|
||||
* @param array $pageParams 分页参数 具体参考:https://www.kancloud.cn/manual/thinkphp6_0/1037638
|
||||
* @param callable|null $callback 复杂查询条件 使用闭包查询 此时建议data留空
|
||||
* @return Paginator
|
||||
* @throws DbException
|
||||
*/
|
||||
public static function findListWithPaginate(array $data = [], array $pageParams = [], callable $callback = null): Paginator
|
||||
{
|
||||
$q = new static();
|
||||
$fields = isset($data['fields']) && !empty($data['fields']) ? $data['fields'] : [];
|
||||
$where = isset($data['where']) && !empty($data['where']) ? $data['where'] : [];
|
||||
$order = isset($data['order']) && !empty($data['order']) ? $data['order'] : [];
|
||||
$limit = $data['size'] ?? 20;
|
||||
|
||||
if (count($where)) {
|
||||
$q = $q->where($where);
|
||||
}
|
||||
|
||||
if (count($fields)) {
|
||||
$q = $q->field($fields);
|
||||
}
|
||||
|
||||
if ($callback) {
|
||||
$q = $callback($q);
|
||||
}
|
||||
|
||||
if (count($order)) {
|
||||
$q = $q->order($order);
|
||||
}
|
||||
|
||||
$pageParams['list_rows'] = $limit;
|
||||
|
||||
return $q->paginate($pageParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询列表
|
||||
*
|
||||
* @param array $simpleWhere 简易查询条件
|
||||
* @param array $fields 查询字段 []表示全部
|
||||
* @param int $page 默认第一页 0不限制
|
||||
* @param int $limit 限制条数 0不限制
|
||||
* @param callable|null $callback 复杂的条件 使用闭包查询
|
||||
* @param array $orders 键值对,排序
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function findList(array $simpleWhere = [], array $fields = [], int $page = 1, int $limit = 0, callable $callback = null, array $orders = []): array
|
||||
{
|
||||
$q = new static();
|
||||
$data = [
|
||||
'total' => 0,
|
||||
'current' => $page,
|
||||
'size' => $limit,
|
||||
'list' => new Collection(),
|
||||
];
|
||||
|
||||
if (count($fields)) {
|
||||
$q = $q->field($fields);
|
||||
}
|
||||
|
||||
if (count($simpleWhere)) {
|
||||
$q = $q->where($simpleWhere);
|
||||
}
|
||||
|
||||
if ($callback) {
|
||||
$q = $callback($q);
|
||||
}
|
||||
|
||||
$data['total'] = $q->count();
|
||||
|
||||
if ($data['total']) {
|
||||
if (count($orders)) {
|
||||
$q = $q->order($orders);
|
||||
}
|
||||
if ($page) {
|
||||
$q = $q->page($page);
|
||||
}
|
||||
if ($limit == 0) {
|
||||
$q = $q->limit(1000);
|
||||
} else {
|
||||
$q = $q->limit($limit);
|
||||
}
|
||||
|
||||
$data['list'] = $q->select();
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取路径
|
||||
*
|
||||
* @param int $pid
|
||||
* @return string
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
*/
|
||||
public static function getPath(int $pid): string
|
||||
{
|
||||
if ($pid == 0) {
|
||||
$path = ',0,';
|
||||
} else {
|
||||
$parent = self::findById($pid);
|
||||
if (empty($parent)) {
|
||||
$path = ',0,';
|
||||
} else {
|
||||
$path = $parent['path'].$parent['id'].',';
|
||||
}
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新路径
|
||||
*
|
||||
* @param int $pid
|
||||
* @param array $data 默认全部 若data不为空 至少包含[id,path, $pidField]
|
||||
* @param string $pidField 父级ID字段名 默认 pid
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function refreshPath(int $pid = 0, array $data = [], string $pidField = 'pid')
|
||||
{
|
||||
$data = !empty($data) ? $data : self::column('id,path,'.$pidField);
|
||||
$updateAllPaths = [];
|
||||
self::recursionPath($pid, $data, $updateAllPaths);
|
||||
|
||||
(new static())->saveAll($updateAllPaths);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取递归最新路径
|
||||
*
|
||||
* @param int $pid
|
||||
* @param array $data 全部数据 尽量传全部数据
|
||||
* @param array $paths
|
||||
*/
|
||||
public static function recursionPath(int $pid, array $data, array &$paths = [])
|
||||
{
|
||||
foreach ($data as $k => $v) {
|
||||
if ($pid == $v['pid']) {
|
||||
$arr = [];
|
||||
$arr['id'] = $v['id'];
|
||||
if ($pid == 0) {
|
||||
$arr['path'] = ',0,';
|
||||
} else {
|
||||
$arr['path'] = $paths[$v['pid']]['path'].$v['pid'].',';
|
||||
}
|
||||
$paths[$v['id']] = $arr;
|
||||
|
||||
unset($data[$k]);
|
||||
|
||||
self::recursionPath($v['id'], $data, $paths);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有后代ID[包含孙级] 仅对拥有path字段的表生效
|
||||
*
|
||||
* @param int $id
|
||||
* @return array
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
*/
|
||||
public static function getAllChildrenIds(int $id): array
|
||||
{
|
||||
$item = self::find($id);
|
||||
if ($item && isset($item['path'])) {
|
||||
$path = $item['path'].$id.',';
|
||||
return self::where('path', 'like', $path.'%')->column('id');
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
<?php
|
||||
namespace app\model;
|
||||
|
||||
use think\model\relation\HasOne;
|
||||
|
||||
class Block extends Base
|
||||
{
|
||||
const BLOCK = 1;//富文本
|
||||
const TEXT= 2;//纯文本
|
||||
const IMG = 3;//图片
|
||||
const GROUP = 4;//多图
|
||||
const FILE = 5;// 文件
|
||||
const VIDEO = 6;// 视频
|
||||
const ING_LIST = 7;// 图文列表
|
||||
|
||||
//获取类型键值对
|
||||
public static function getTypes()
|
||||
{
|
||||
return [
|
||||
self::BLOCK => '富文本',
|
||||
self::TEXT => '纯文本',
|
||||
self::IMG => '图片',
|
||||
self::GROUP => '多图',
|
||||
self::FILE => '文件',
|
||||
self::VIDEO => '视频',
|
||||
self::ING_LIST => '图文列表',
|
||||
];
|
||||
}
|
||||
|
||||
//根据键值和类目获取数据
|
||||
public static function getByKeyword($keyword, $categoryId)
|
||||
{
|
||||
return self::where('keyword', $keyword)->where('category_id', $categoryId)->findOrEmpty()->toArray();
|
||||
}
|
||||
|
||||
//根据栏目ID获取块列表
|
||||
public static function getByCategoryId($categoryId)
|
||||
{
|
||||
|
||||
if($categoryId <= 0){
|
||||
return [];
|
||||
}
|
||||
|
||||
$items = self::where('category_id', $categoryId)
|
||||
->order('id desc')
|
||||
->select()
|
||||
->toArray();
|
||||
if(empty($items)){
|
||||
return [];
|
||||
}
|
||||
$data = [];
|
||||
foreach($items as $item){
|
||||
$data[$item['name']] = $item;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
//获取在使用中的文件
|
||||
public static function getFilesInUse()
|
||||
{
|
||||
$items = self::whereIn('type', [self::IMG, self::GROUP, self::VIDEO, self::TEXT])
|
||||
->select()
|
||||
->toArray();
|
||||
$data = [];
|
||||
foreach($items as $item){
|
||||
if($item['type'] == self::IMG){
|
||||
$file = trim($item['value']);
|
||||
if(!empty($file)){
|
||||
$key = getKeyByPath($file);
|
||||
$data[$key] = $file;
|
||||
}
|
||||
}elseif($item['type'] == self::GROUP){
|
||||
$val = trim($item['value']);
|
||||
if (!empty($val) && $val != '[]' && $val != 'null') {
|
||||
$files = json_decode($val, true);
|
||||
foreach($files as $file){
|
||||
$src = trim($file['src']);
|
||||
if(!empty($src)){
|
||||
$key = getKeyByPath($src);
|
||||
$data[$key] = $src;
|
||||
}
|
||||
}
|
||||
}
|
||||
}elseif($item['type'] == self::VIDEO){
|
||||
$video = trim($item['value']);
|
||||
if(!empty($video)){
|
||||
$key = getKeyByPath($video);
|
||||
$data[$key] = $video;
|
||||
}
|
||||
$img = trim($item['img']);
|
||||
if(!empty($img)){
|
||||
$key = getKeyByPath($img);
|
||||
$data[$key] = $img;
|
||||
}
|
||||
}elseif($item['type'] == self::TEXT){
|
||||
$imgs = getImageUrlFromText($item['value']);
|
||||
if(!empty($imgs)){
|
||||
$data = array_merge($data, $imgs);
|
||||
}
|
||||
$videos = getVideoUrlFromText($item['value']);
|
||||
if(!empty($videos)){
|
||||
$data = array_merge($data, $videos);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 栏目
|
||||
*
|
||||
* @return HasOne
|
||||
*/
|
||||
public function category(): HasOne
|
||||
{
|
||||
return $this->HasOne(ArchivesCategory::class, 'id', 'category_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置内容
|
||||
* */
|
||||
public function setContentAttr($value,$data)
|
||||
{
|
||||
if($data['type'] == self::ING_LIST){
|
||||
$content = array_column($value,null,"sort");
|
||||
$contentKey = array_keys($content);
|
||||
sort($contentKey);
|
||||
$contentData =[];
|
||||
foreach ($contentKey as $ck){
|
||||
$contentData[]= $content[$ck];
|
||||
}
|
||||
return json_encode($contentData);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置内容
|
||||
* */
|
||||
public function getContentAttr($value,$data)
|
||||
{
|
||||
if($data['type'] == self::ING_LIST){
|
||||
return json_decode($value,true)
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace app\model;
|
||||
|
||||
/**
|
||||
* 配置表
|
||||
* Class Config
|
||||
* @package app\model
|
||||
*/
|
||||
class Config extends Base
|
||||
{
|
||||
// 路径都没有加前缀/ 需要的地方自行添加
|
||||
// 小程序内容详情路径----新闻内容详情 包含: 关于我们模型、普通文章模型
|
||||
public const MINI_PATH_ARCHIVES = 'pagesB/articleDetail/articleDetail';
|
||||
// 小程序病种详情路径----病种内容详情(前端叫法) 包含: 非我们模型、普通文章模型的其他模型 如问题文章、科普视频等
|
||||
public const MINI_PATH_PROBLEM = 'pagesB/problemDetail/problemDetail';
|
||||
// 小程序预约列表界面
|
||||
public const MINI_PATH_APPOINTMENT = 'pagesA/makeAnPppointment/makeAnPppointment';
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
namespace app\model;
|
||||
|
||||
/**
|
||||
* 配置分组表
|
||||
* Class ConfigGroup
|
||||
* @package app\model
|
||||
*/
|
||||
class ConfigGroup extends Base
|
||||
{
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
namespace app\model;
|
||||
|
||||
use think\model\relation\HasOne;
|
||||
|
||||
/**
|
||||
* 意见与反馈
|
||||
*
|
||||
* Class Feedback
|
||||
* @package app\model
|
||||
*/
|
||||
class Feedback extends Base
|
||||
{
|
||||
/**
|
||||
* 关联提交者
|
||||
*
|
||||
* @return HasOne
|
||||
*/
|
||||
public function account(): HasOne
|
||||
{
|
||||
return $this->hasOne(Account::class, 'id', 'account_id');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
<?php
|
||||
|
||||
namespace app\model;
|
||||
|
||||
use app\service\AliOss;
|
||||
use Exception;
|
||||
use think\facade\Config;
|
||||
use think\Image;
|
||||
|
||||
class File extends Base
|
||||
{
|
||||
const IMG = 'image';
|
||||
const VIDEO = 'video';
|
||||
const FILE = 'file';
|
||||
|
||||
//获取文件类型
|
||||
public static function getTypes()
|
||||
{
|
||||
return [
|
||||
'image' => '图片',
|
||||
'video' => '视频',
|
||||
'file' => '文件'
|
||||
];
|
||||
}
|
||||
|
||||
//获取文件列表
|
||||
public static function getList($type = '', $page = 1, $per = 20)
|
||||
{
|
||||
$limit = ($page - 1) * $per;
|
||||
if ($type != '') {
|
||||
if (!in_array($type, array_keys(self::getTypes()))) {
|
||||
return [];
|
||||
}
|
||||
$items = self::where('type', $type)
|
||||
->order('id desc');
|
||||
} else {
|
||||
$items = self::order('id desc');
|
||||
}
|
||||
$items = $items->limit($limit, $per)->select()->toArray();
|
||||
foreach ($items as &$item) {
|
||||
$item['sizeStr'] = sizeToStr($item['size']);
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
|
||||
//获取分页列表
|
||||
public static function getListPage($type = '', $per = 20)
|
||||
{
|
||||
if ($type != '') {
|
||||
if (!in_array($type, array_keys(self::getTypes()))) {
|
||||
return [];
|
||||
}
|
||||
return self::where('type', $type)
|
||||
->order('id desc')
|
||||
->paginate([
|
||||
'list_rows' => $per,
|
||||
'query' => [
|
||||
'type' => $type
|
||||
]
|
||||
], false);
|
||||
} else {
|
||||
return self::order('id desc')
|
||||
->paginate([
|
||||
'list_rows' => $per
|
||||
], false);
|
||||
}
|
||||
}
|
||||
|
||||
//添加,$w_h图片尺寸大小,单位像素,只对type=img时有效
|
||||
public static function add($file, $src, $md5, $type = 'image')
|
||||
{
|
||||
$realPath = public_path().ltrim($src, '/');
|
||||
$oss = false;
|
||||
if (is_file($realPath) && $type == 'image') {
|
||||
$img = Image::open($realPath);
|
||||
list($w, $h) = $img->size();
|
||||
$w_h = $w.'px * '.$h.'px';
|
||||
} else {
|
||||
$w_h = '';
|
||||
}
|
||||
|
||||
$now = date('Y-m-d H:i:s');
|
||||
Attachment::pathDirHandle($src);
|
||||
|
||||
Config::load('extra/base', 'base');
|
||||
$baseConfig = config('base');
|
||||
if (isset($baseConfig['oss']) && $baseConfig['oss'] == 'true') {
|
||||
$ossObject = AliOss::instance();
|
||||
try {
|
||||
$pathInfo = pathinfo($src);
|
||||
|
||||
$ossConfig = AliOss::config();
|
||||
$bucket = $ossConfig['bucket'];
|
||||
//是否存在
|
||||
if (!$ossObject->doesObjectExist($bucket, ltrim($src, '/'))) {
|
||||
//创建目录
|
||||
$ossObject->createObjectDir($bucket, ltrim($pathInfo['dirname'], '/'));
|
||||
|
||||
$ossObject->uploadFile($bucket, ltrim($src, '/'), $realPath);
|
||||
}
|
||||
$oss = true;
|
||||
} catch (Exception $e) {
|
||||
\think\facade\Log::error('阿里云OSS上传文件失败 '.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 将src中路径创建
|
||||
return self::create([
|
||||
'type' => $type,
|
||||
'name' => $file->getOriginalName(),
|
||||
'md5' => $md5,
|
||||
'src' => $src,
|
||||
'path' => isset(pathinfo($src)['dirname']) ? pathinfo($src)['dirname'].'/' : '',
|
||||
'size' => $file->getSize(),
|
||||
'suffix' => $file->getOriginalExtension(),
|
||||
'mime_type' => $file->getOriginalMime(),
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now,
|
||||
'is_oss' => $oss,
|
||||
'w_h' => $w_h
|
||||
]);
|
||||
}
|
||||
|
||||
//获取所有记录
|
||||
public static function getAll()
|
||||
{
|
||||
return self::select()->toArray();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
namespace app\model;
|
||||
|
||||
class Link extends Base
|
||||
{
|
||||
public static function delByIds($ids)
|
||||
{
|
||||
return self::whereIn('id', $ids)->delete();
|
||||
}
|
||||
|
||||
//获取友情链接
|
||||
public static function getList()
|
||||
{
|
||||
return self::order('sort asc')
|
||||
->select()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
public static function onAfterInsert($item)
|
||||
{
|
||||
$item->sort = $item->id;
|
||||
$item->save();
|
||||
}
|
||||
|
||||
//获取友情链接涉及到的文件
|
||||
public static function getFilesInUse()
|
||||
{
|
||||
$items = self::select()->toArray();
|
||||
$data = [];
|
||||
foreach($items as $item){
|
||||
$src = trim($item['src']);
|
||||
if(!empty($src)){
|
||||
$key = getKeyByPath($src);
|
||||
$data[$key] = $src;
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
namespace app\model;
|
||||
|
||||
use think\model\relation\HasOne;
|
||||
|
||||
class Log extends Base
|
||||
{
|
||||
//记录操作日志
|
||||
public static function write($controller, $action, $content, $requestType = '')
|
||||
{
|
||||
$auth = session('auth');
|
||||
return self::create([
|
||||
'member_id' => $auth['user_id'] ?? 0,
|
||||
'name' => $auth['username'] ?? 0,
|
||||
'ip' => request()->ip(),
|
||||
'create_time' => time(),
|
||||
'controller' => $controller,
|
||||
'request_type' => $requestType,
|
||||
'action' => $action,
|
||||
'content' => $content
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HasOne
|
||||
*/
|
||||
public function memberName(): HasOne
|
||||
{
|
||||
return $this->hasOne(Member::class, 'id', 'member_id')->bind(['operator' => 'nickname']);
|
||||
}
|
||||
|
||||
public function getCreateTimeAttr($value)
|
||||
{
|
||||
if (empty($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return date('Y-m-d H:i:s', $value);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
<?php
|
||||
namespace app\model;
|
||||
|
||||
class LoginLog extends Base
|
||||
{
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace app\model;
|
||||
|
||||
class Member extends Base
|
||||
{
|
||||
public const STATUS_NORMAL = 1;//正常
|
||||
public const STATUS_DISABLE = 0;//禁用
|
||||
|
||||
public static function getList($limit = 40)
|
||||
{
|
||||
return self::alias('m')
|
||||
->leftjoin('auth_group g', 'g.id=m.group_id')
|
||||
->field('m.id,m.username,m.login_time,m.group_id,g.title')
|
||||
->order('m.id', 'asc')
|
||||
->paginate($limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据角色分组返回用户
|
||||
* @param int $groupId 角色分组ID
|
||||
* @param int $limit 每页数量
|
||||
*/
|
||||
public static function getListByGroup($groupId, $limit = 40)
|
||||
{
|
||||
return self::alias('m')
|
||||
->leftjoin('auth_group g', 'g.id=m.group_id')
|
||||
->field('m.id,m.username,m.login_time,m.group_id,g.title')
|
||||
->where('m.group_id', '=', $groupId)
|
||||
->order('m.id', 'asc')
|
||||
->paginate($limit);
|
||||
}
|
||||
|
||||
//根据用户名获取管理账号
|
||||
public static function getByUserName($username)
|
||||
{
|
||||
return self::where('username', trim($username))
|
||||
->findOrEmpty()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
//根据ID获取管理账户和相关权限
|
||||
public static function getMemberAndRulesByID($memberId)
|
||||
{
|
||||
return self::alias('m')
|
||||
->join('auth_group g', 'm.group_id = g.id', 'LEFT')
|
||||
->field('m.group_id,g.rules')
|
||||
->where('m.id', $memberId)
|
||||
->findOrEmpty()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
public static function updateCates($id, $cates)
|
||||
{
|
||||
$cates = implode(',', $cates);
|
||||
$data = ['cates' => $cates];
|
||||
self::updateById($id, $data);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
namespace app\model;
|
||||
|
||||
use Exception;
|
||||
|
||||
class Menu extends Base
|
||||
{
|
||||
public const SHOW_YES = 1;
|
||||
public const SHOW_NO = 0;
|
||||
|
||||
public const STATUS_NORMAL = 1;
|
||||
public const STATUS_DISABLE = 0;
|
||||
|
||||
public const TYPE_MENU = 'menu';
|
||||
public const TYPE_ACTION = 'action';
|
||||
|
||||
/**
|
||||
* 默认操作
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public static function defaultAction(): array
|
||||
{
|
||||
return [
|
||||
'index' => '查看',
|
||||
'add' => '添加',
|
||||
'edit' => '编辑',
|
||||
'del' => '删除',
|
||||
'sort' => '排序',
|
||||
'modify' => '属性设置',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 自从生成常规操作权限
|
||||
*
|
||||
* @param int $id
|
||||
* @param string $name
|
||||
* @param string $path
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function generate(int $id, string $name, string $path)
|
||||
{
|
||||
$actions = self::defaultAction();
|
||||
$delete = [];
|
||||
$insert = [];
|
||||
$created = date('Y-m-d H:i:s');
|
||||
foreach ($actions as $key => $action) {
|
||||
$name = explode(':', $name)[0];
|
||||
$delete[] = $name.':'.$key;
|
||||
|
||||
$arr = [];
|
||||
$arr['title'] = $action;
|
||||
$arr['pid'] = $id;
|
||||
$arr['name'] = $name.':'.$key;
|
||||
$arr['type'] = self::TYPE_ACTION;
|
||||
$arr['path'] = $path.$id.',';
|
||||
$arr['remark'] = sprintf("自动生成[%s][%s]操作", $name, $action);
|
||||
$arr['created_at'] = $created;
|
||||
|
||||
$insert[] = $arr;
|
||||
}
|
||||
|
||||
//删除已有常规操作
|
||||
self::where('pid', $id)->whereIn('name', $delete)->delete();
|
||||
|
||||
//新增常规操作
|
||||
(new self())->saveAll($insert);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
namespace app\model;
|
||||
|
||||
class Model extends Base
|
||||
{
|
||||
//获取模型列表
|
||||
public static function getList()
|
||||
{
|
||||
return self::order('sort asc')
|
||||
->select()
|
||||
->toArray();
|
||||
}
|
||||
public static function onAfterInsert($model)
|
||||
{
|
||||
$model->sort = $model->id;
|
||||
$model->save();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
namespace app\model;
|
||||
|
||||
class Role extends Base
|
||||
{
|
||||
public const STATUS_NORMAL = 1; //正常
|
||||
public const STATUS_DISABLE = 0;//禁用
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
namespace app\model;
|
||||
|
||||
class Rules extends Base
|
||||
{
|
||||
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
namespace app\model;
|
||||
|
||||
use think\Model;
|
||||
use think\model\relation\HasOne;
|
||||
|
||||
class Sku extends Base
|
||||
{
|
||||
// SKU 状态
|
||||
public const STATUS_ON = '1'; // 出售中
|
||||
public const STATUS_OFF = '0'; // 已下架
|
||||
|
||||
// SKU 积分兑换限制
|
||||
public const ALLOW_ONLY_BUY = '0';
|
||||
public const ALLOW_BUY_AND_SCORE = '1';
|
||||
public const ALLOW_ONLY_SCORE = '2';
|
||||
|
||||
|
||||
public static function statusTextList(): array
|
||||
{
|
||||
return [
|
||||
(string)self::STATUS_ON => '在售',
|
||||
(string)self::STATUS_OFF => '已下架',
|
||||
];
|
||||
}
|
||||
|
||||
public static function allowScoreTextList(): array
|
||||
{
|
||||
return [
|
||||
(string)self::ALLOW_ONLY_BUY => '购买',
|
||||
(string)self::ALLOW_BUY_AND_SCORE => '购买或积分兑换',
|
||||
(string)self::ALLOW_ONLY_SCORE => '积分兑换',
|
||||
];
|
||||
}
|
||||
|
||||
public static function onAfterInsert(Model $item)
|
||||
{
|
||||
$item->sort = $item->id;
|
||||
$item->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* 模型关联:商品分类
|
||||
*
|
||||
* @return HasOne
|
||||
*/
|
||||
public function category(): HasOne
|
||||
{
|
||||
return $this->hasOne(SkuCategory::class, 'id', 'category_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 SKU coding
|
||||
* 年份 + 2位随机码 + 时间戳(秒[10位] + 微妙[4位])
|
||||
* @length 18
|
||||
*/
|
||||
public static function generateCoding(): string
|
||||
{
|
||||
$randStr = str_pad(mt_rand(1,99), 2, '0', STR_PAD_LEFT);
|
||||
$prefix = date('y').$randStr;
|
||||
$mt = microtime(true);
|
||||
list($time, $micro) = explode('.', $mt);
|
||||
$micro = str_pad($micro, 4, '0', STR_PAD_RIGHT);
|
||||
$micro = substr($micro, 0, 4);
|
||||
|
||||
return $prefix.$time.$micro;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HasOne
|
||||
*/
|
||||
public function spu(): HasOne
|
||||
{
|
||||
return $this->hasOne(Spu::class, 'id', 'spu_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联活动商品
|
||||
*
|
||||
* @return HasOne
|
||||
*/
|
||||
public function activity(): HasOne
|
||||
{
|
||||
return $this->hasOne(SpuActivity::class, 'id', 'spu_activity_id');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
namespace app\model;
|
||||
|
||||
class Slide extends Base
|
||||
{
|
||||
public static function delByIds($ids)
|
||||
{
|
||||
return self::whereIn('id', $ids)->delete();
|
||||
}
|
||||
|
||||
//获取幻灯片列表
|
||||
public static function getList()
|
||||
{
|
||||
return self::order("sort asc")
|
||||
->select()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
public static function onAfterInsert($slide)
|
||||
{
|
||||
$slide->sort = $slide->id;
|
||||
$slide->save();
|
||||
}
|
||||
|
||||
//获取轮播图涉及到的文件
|
||||
public static function getFilesInUse()
|
||||
{
|
||||
$items = self::select()->toArray();
|
||||
$data = [];
|
||||
foreach ($items as $item) {
|
||||
$src = trim($item['src']);
|
||||
if (!empty($src)) {
|
||||
$key = getKeyByPath($src);
|
||||
$data[$key] = $src;
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
namespace app\model;
|
||||
|
||||
/**
|
||||
* 轮播图显示位置
|
||||
*
|
||||
* Class SlidePosition
|
||||
* @package app\model
|
||||
*/
|
||||
class SlidePosition extends Base
|
||||
{
|
||||
const home_position = "home-banner";
|
||||
public static function allPosition()
|
||||
{
|
||||
return self::select();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
namespace app\model;
|
||||
|
||||
class SmsLog extends Base
|
||||
{
|
||||
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
namespace app\model;
|
||||
|
||||
class System extends Base
|
||||
{
|
||||
//获取友情链接上传图片尺寸
|
||||
public static function getLinkImageSize()
|
||||
{
|
||||
$system = self::getSystem();
|
||||
if(!empty($system['link_w']) && !empty($system['link_h'])){
|
||||
$linkSize = $system['link_w'] . '像素 X ' . $system['link_h'] . '像素';
|
||||
}else{
|
||||
$linkSize = '';
|
||||
}
|
||||
return $linkSize;
|
||||
}
|
||||
//获取幻灯片上传尺寸
|
||||
public static function getSlideImageSize()
|
||||
{
|
||||
$system = self::getSystem();
|
||||
if(!empty($system['slide_w']) && !empty($system['slide_h'])){
|
||||
$slideSize = $system['slide_w'] . '像素 X ' . $system['slide_h'] . '像素';
|
||||
}else{
|
||||
$slideSize = '';
|
||||
}
|
||||
if(!empty($system['slide_mobile_w']) && !empty($system['slide_mobile_h'])){
|
||||
$slideMobileSize = $system['slide_mobile_w'] . '像素 X ' . $system['slide_mobile_h'] . '像素';
|
||||
}else{
|
||||
$slideMobileSize = '';
|
||||
}
|
||||
return [
|
||||
'slide_size' => $slideSize,
|
||||
'slide_mobile_size' => $slideMobileSize
|
||||
];
|
||||
}
|
||||
//获取文章图片尺寸
|
||||
public static function getArticleImageSize()
|
||||
{
|
||||
$system = self::getSystem();
|
||||
if(!empty($system['article_w']) && !empty($system['article_h'])){
|
||||
$articleSize = $system['article_w'] . '像素 X ' . $system['article_h'] . '像素';
|
||||
}else{
|
||||
$articleSize = '';
|
||||
}
|
||||
return $articleSize;
|
||||
}
|
||||
//获取系统配置信息
|
||||
public static function getSystem()
|
||||
{
|
||||
return self::order('id asc')
|
||||
->findOrEmpty()
|
||||
->toArray();
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace app\repository;
|
||||
|
||||
use app\exception\RepositoryException;
|
||||
use app\model\AccountRecord;
|
||||
use app\model\Archives;
|
||||
use app\model\ArchivesCategory;
|
||||
use app\model\ArchivesModel;
|
||||
use app\model\Course;
|
||||
use app\model\Diary;
|
||||
use app\model\Disease;
|
||||
use app\model\DoctorRelation;
|
||||
use Exception;
|
||||
use think\Model;
|
||||
use think\Collection;
|
||||
use app\service\Repository;
|
||||
use think\db\exception\DbException;
|
||||
use think\db\exception\DataNotFoundException;
|
||||
use think\db\exception\ModelNotFoundException;
|
||||
|
||||
/**
|
||||
* 内容 相关
|
||||
*
|
||||
* Class ArchivesRepository
|
||||
* @package app\repository
|
||||
* @method self getInstance(Model $model = null) static
|
||||
*/
|
||||
class ArchivesRepository extends Repository
|
||||
{
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace app\repository;
|
||||
|
||||
use think\Model;
|
||||
use app\model\Block;
|
||||
use app\model\Category;
|
||||
use app\service\Repository;
|
||||
|
||||
/**
|
||||
* 自定义领域 相关
|
||||
*
|
||||
* Class BlockRepository
|
||||
* @package app\repository
|
||||
* @method self getInstance(Model $model = null) static
|
||||
*/
|
||||
class BlockRepository extends Repository
|
||||
{
|
||||
public const CATEGORY_DTC = 'dtc'; //dtc认证
|
||||
public const CATEGORY_NEWS = 'news'; //活动信息
|
||||
public const CATEGORY_CONTACT = 'contact'; //联系我们
|
||||
public const CATEGORY_ABOUT = 'about'; //关于我们
|
||||
|
||||
|
||||
/**
|
||||
* @param int $cateId
|
||||
* @return array
|
||||
*/
|
||||
public function getByCateId(int $cateId): array
|
||||
{
|
||||
return Block::getByCategoryId($cateId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分类标识
|
||||
*
|
||||
* @param string $name 通过栏目分类标识获取Block
|
||||
* @return array
|
||||
*/
|
||||
public function getByCateName(string $name): array
|
||||
{
|
||||
$categoryId = Category::where('name', $name)->value('id');
|
||||
if ($categoryId) {
|
||||
return Block::getByCategoryId($categoryId);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
namespace app\repository;
|
||||
|
||||
use app\traits\cms\ArticleTrait;
|
||||
use app\traits\cms\MenuTrait;
|
||||
use tauthz\facade\Enforcer;
|
||||
use think\db\exception\DataNotFoundException;
|
||||
use think\db\exception\DbException;
|
||||
use think\db\exception\ModelNotFoundException;
|
||||
use think\Model;
|
||||
use app\service\Repository;
|
||||
|
||||
/**
|
||||
* CMS 基础功能仓储
|
||||
*
|
||||
* Class CmsRepository
|
||||
* @package app\repository
|
||||
* @method self getInstance(Model $model = null) static
|
||||
*/
|
||||
class CmsRepository extends Repository
|
||||
{
|
||||
|
||||
use MenuTrait;
|
||||
use ArticleTrait;
|
||||
|
||||
/**
|
||||
* xmSelect下拉列表格式处理
|
||||
* 如 [['title' => 'aa', 'value' => 1, 'selected' => true, 'prefix' => ' ']]
|
||||
*
|
||||
* @param array $data 待处理的数据
|
||||
* @param string $symbol 分隔符号 默认
|
||||
* @param int $repeatNum 重复次数 默认4
|
||||
* @return array
|
||||
*/
|
||||
public function handleSelectedList(array $data, string $symbol = ' ', int $repeatNum = 4): array
|
||||
{
|
||||
$list = [];
|
||||
foreach ($data as $item) {
|
||||
$level = $item['level'] ?? 0;
|
||||
$arr = $item;
|
||||
$arr['children'] = $arr['children'] ?? [];
|
||||
$arr['prefix'] = str_repeat($symbol, $level * $repeatNum);
|
||||
$list[] = $arr;
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取后台用户权限列表
|
||||
*
|
||||
* @param int $accountId
|
||||
* @return array
|
||||
*/
|
||||
public function getUserRules(int $accountId): array
|
||||
{
|
||||
$rules = [];
|
||||
$roles = Enforcer::getRolesForUser($accountId);
|
||||
foreach ($roles as $role) {
|
||||
$rules = array_merge($rules, Enforcer::getPermissionsForUser($role));
|
||||
}
|
||||
|
||||
$ruleNameList = [];
|
||||
foreach ($rules as $rule) {
|
||||
if (isset($rule[2])) {
|
||||
$ruleNameList[] = $rule[1].':'.$rule[2];
|
||||
} else {
|
||||
$ruleNameList[] = $rule[1];
|
||||
}
|
||||
}
|
||||
|
||||
return array_unique($ruleNameList);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
<?php
|
||||
|
||||
namespace app\repository;
|
||||
|
||||
use app\model\SmsLog;
|
||||
use app\service\Repository;
|
||||
use app\service\Sms;
|
||||
use Exception;
|
||||
use think\db\exception\DataNotFoundException;
|
||||
use think\db\exception\DbException;
|
||||
use think\db\exception\ModelNotFoundException;
|
||||
use think\facade\Log;
|
||||
use think\Model;
|
||||
|
||||
/**
|
||||
* 通用域 相关操作
|
||||
*
|
||||
* Class CommonRepository
|
||||
* @package app\repository
|
||||
* @method self getInstance(Model $model = null) static
|
||||
*/
|
||||
class CommonRepository extends Repository
|
||||
{
|
||||
public const SMS_TYPE_REGISTER = 'register';//注册
|
||||
public const SMS_TYPE_LOGIN = 'login';//登录
|
||||
public const SMS_TYPE_BINDING = 'binding';//绑定
|
||||
public const SMS_TYPE_EDIT_PASSWORD = 'edit_password';//修改密码
|
||||
|
||||
public const SMS_STATUS_OK = 1;//短信验证通过
|
||||
public const SMS_STATUS_WAITING = 0;//验证码待检测
|
||||
|
||||
/**
|
||||
* 发送短信
|
||||
*
|
||||
* @param string $phone
|
||||
* @param string $type
|
||||
* @return bool
|
||||
*/
|
||||
public function sendSms(string $phone, string $type): bool
|
||||
{
|
||||
try {
|
||||
$now = time();
|
||||
$res = SmsLog::create([
|
||||
'phone' => $phone,
|
||||
'type' => $type,
|
||||
'code' => rand(100000, 999999),
|
||||
'created_at' => date('Y-m-d H:i:s', $now),
|
||||
'expired_at' => date('Y-m-d H:i:s', $now + 5 * 60),
|
||||
]);
|
||||
|
||||
$res = Sms::send($phone, $res['code']);
|
||||
if (is_bool($res)) {
|
||||
return $res;
|
||||
}
|
||||
|
||||
Log::error(json_encode($res, JSON_FORCE_OBJECT));
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
Log::error(sprintf("[发送短信验证码失败]%s:%d %s", $e->getFile(), $e->getLine(), $e->getMessage()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查短信验证码
|
||||
*
|
||||
* @param string $phone
|
||||
* @param string $code
|
||||
* @param string $type
|
||||
* @return bool
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
*/
|
||||
public function checkSms(string $phone, string $code, string $type): bool
|
||||
{
|
||||
$item = (new SmsLog())->where('phone', $phone)
|
||||
->where('type', $type)
|
||||
->where('code', $code)
|
||||
->order('created_at', 'desc')
|
||||
->order('id', 'desc')
|
||||
->find();
|
||||
if (!$item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($item['expired_at'] < date('Y-m-d H:i:s')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $item->save(['status' => self::SMS_STATUS_OK]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 日志记录
|
||||
*
|
||||
* @param string $msg
|
||||
* @param Exception|null $e
|
||||
* @param string $level
|
||||
* @param string $channel
|
||||
*/
|
||||
public static function log(string $msg, Exception $e = null, string $level = 'error', string $channel = 'file')
|
||||
{
|
||||
if ($e != null) {
|
||||
$msg = sprintf("[%s]%s:%s %s", $msg, $e->getFile(), $e->getLine(), $e->getMessage());
|
||||
} else {
|
||||
$msg = sprintf("%s", $msg);
|
||||
|
||||
}
|
||||
Log::channel($channel)->$level($msg);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,212 @@
|
|||
<?php
|
||||
namespace app\repository;
|
||||
|
||||
use app\model\SlidePosition;
|
||||
use Exception;
|
||||
use think\Model;
|
||||
use app\model\Slide;
|
||||
use think\Collection;
|
||||
use app\service\Repository;
|
||||
use think\db\exception\DbException;
|
||||
use think\db\exception\DataNotFoundException;
|
||||
use think\db\exception\ModelNotFoundException;
|
||||
|
||||
/**
|
||||
* 运营领域 相关
|
||||
*
|
||||
* Class OperationRepository
|
||||
* @package app\repository
|
||||
* @method self getInstance(Model $model = null) static
|
||||
*/
|
||||
class OperationRepository extends Repository
|
||||
{
|
||||
/**
|
||||
* 轮播位置
|
||||
*
|
||||
* @return string[][]
|
||||
*/
|
||||
public function slidePositions(): array
|
||||
{
|
||||
try {
|
||||
return SlidePosition::allPosition()->toArray();
|
||||
} catch (Exception $e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取轮播
|
||||
*
|
||||
* @param int $id
|
||||
* @return array|Model|null
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
*/
|
||||
public function findSlideById(int $id)
|
||||
{
|
||||
return Slide::findById($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 轮播列表
|
||||
*
|
||||
* @param array $where
|
||||
* @param array $fields
|
||||
* @param int $page
|
||||
* @param int $size
|
||||
* @param callable|null $call
|
||||
* @param array $orders
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function slideList(array $where=[], array $fields=[], int $page=1, int $size=20, callable $call=null, array $orders=[]): array
|
||||
{
|
||||
return Slide::findList($where, $fields, $page, $size, $call, $orders);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新轮播
|
||||
*
|
||||
* @param array $data
|
||||
* @param int $id
|
||||
* @return bool
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
*/
|
||||
public function updateSlide(array $data, int $id): bool
|
||||
{
|
||||
$item = (new Slide())->where('id', $id)->find();
|
||||
if ($item) {
|
||||
return $item->save($data);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建轮播
|
||||
*
|
||||
* @param array $data
|
||||
* @return Slide
|
||||
*/
|
||||
public function createSlide(array $data): Slide
|
||||
{
|
||||
$data['created_at'] = date('y-m-d H:i:s');
|
||||
return Slide::create($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除轮播图
|
||||
*
|
||||
* @param array $ids
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteSlides(array $ids): bool
|
||||
{
|
||||
return Slide::deleteByIds($ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* 轮播位置是否存在
|
||||
*
|
||||
* @param string $position
|
||||
* @param int $exceptId 需要排除的显示位置ID
|
||||
* @return bool
|
||||
*/
|
||||
public function slidePositionExists(string $position, int $exceptId = 0): bool
|
||||
{
|
||||
return (new SlidePosition())->when($exceptId > 0, function ($q) use ($exceptId) {
|
||||
$q->where('id', '<>', $exceptId);
|
||||
})->where('key', $position)->count() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据显示位置查询相关的轮播图信息
|
||||
*
|
||||
* @param string $position 轮播图位置标识
|
||||
* @param int $size 限制查询数量
|
||||
* @throws Exception
|
||||
*/
|
||||
public function slideListByPosition(string $position, int $size=0): ?Collection
|
||||
{
|
||||
$where[] = ['position', '=', $position];
|
||||
$orders = ['sort'=>'asc'];
|
||||
return Slide::findList($where, [], 1, $size, null, $orders)['list'];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 轮播显示位置列表
|
||||
*
|
||||
* @param array $where
|
||||
* @param array $fields
|
||||
* @param int $page
|
||||
* @param int $size
|
||||
* @param callable|null $call
|
||||
* @param array $orders
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function slidePositionList(array $where=[], array $fields=[], int $page=1, int $size=20, callable $call=null, array $orders=[]): array
|
||||
{
|
||||
return SlidePosition::findList($where, $fields, $page, $size, $call, $orders);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新轮播位置
|
||||
*
|
||||
* @param array $data
|
||||
* @param int $id
|
||||
* @return bool
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
*/
|
||||
public function updateSlidePosition(array $data, int $id): bool
|
||||
{
|
||||
$item = (new SlidePosition())->where('id', $id)->find();
|
||||
if ($item) {
|
||||
$item->save($data);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建轮播位置
|
||||
*
|
||||
* @param array $data
|
||||
* @return SlidePosition
|
||||
*/
|
||||
public function createSlidePosition(array $data): SlidePosition
|
||||
{
|
||||
$data['created_at'] = date('y-m-d H:i:s');
|
||||
return SlidePosition::create($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除轮播位置
|
||||
*
|
||||
* @param array $ids
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteSlidePositions(array $ids): bool
|
||||
{
|
||||
return SlidePosition::deleteByIds($ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取轮播位置
|
||||
*
|
||||
* @param int $id
|
||||
* @return array|Model|null
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
*/
|
||||
public function findSlidePositionById(int $id)
|
||||
{
|
||||
return SlidePosition::findById($id);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
use tauthz\TauthzService;
|
||||
|
||||
// 系统服务定义文件
|
||||
// 服务在完成全局初始化之后执行
|
||||
return [
|
||||
TauthzService::class,
|
||||
];
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
namespace app\service;
|
||||
|
||||
use OSS\Core\OssException;
|
||||
use OSS\OssClient;
|
||||
use think\facade\Config;
|
||||
use think\facade\Log;
|
||||
|
||||
class AliOss
|
||||
{
|
||||
private static $oss = null;
|
||||
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
private function __clone()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 阿里云OSS
|
||||
*
|
||||
*/
|
||||
public static function config()
|
||||
{
|
||||
Config::load('extra/alioss', 'alioss');
|
||||
return config('alioss');
|
||||
}
|
||||
|
||||
// 阿里云OSS实例 单例模式
|
||||
public static function instance(): ?OssClient
|
||||
{
|
||||
if (self::$oss == null) {
|
||||
try {
|
||||
$conf = self::config();
|
||||
self::$oss = new OssClient($conf['accessKeyId'], $conf['accessKeySecret'], $conf['endpoint']);
|
||||
} catch (OssException $e) {
|
||||
Log::error('实例化阿里云OSS失败: ' . $e->getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return self::$oss;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
namespace app\service;
|
||||
|
||||
use Yansongda\Pay\Pay;
|
||||
use Yansongda\Pay\Log;
|
||||
use think\facade\Config;
|
||||
|
||||
class Alipay
|
||||
{
|
||||
private static $app = null;
|
||||
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
private function __clone()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付宝配置
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function config(): array
|
||||
{
|
||||
Config::load('extra/alipay', 'alipay');
|
||||
$conf = config('alipay');
|
||||
return [
|
||||
'app_id' => trim($conf['appId']),
|
||||
'notify_url' => trim($conf['notify_url']),
|
||||
'return_url' => trim($conf['return_url']) ?? trim($conf['notify_url']),
|
||||
'ali_public_key' => trim($conf['aliPubKey']),//注意 这里的是支付宝的公钥
|
||||
// 加密方式: **RSA2**
|
||||
'private_key' => trim($conf['priKey']),
|
||||
// 使用公钥证书模式,请配置下面两个参数,同时修改ali_public_key为以.crt结尾的支付宝公钥证书路径,
|
||||
// 如(./cert/alipayCertPublicKey_RSA2.crt)
|
||||
// 'app_cert_public_key' => './cert/appCertPublicKey.crt', //应用公钥证书路径
|
||||
// 'alipay_root_cert' => './cert/alipayRootCert.crt', //支付宝根证书路径
|
||||
'log' => [ // optional
|
||||
'file' => './logs/alipay.log',
|
||||
'level' => 'debug', // 建议生产环境等级调整为 info,开发环境为 debug
|
||||
'type' => 'single', // optional, 可选 daily.
|
||||
'max_file' => 30, // optional, 当 type 为 daily 时有效,默认 30 天
|
||||
],
|
||||
'http' => [ // optional
|
||||
'timeout' => 5.0,
|
||||
'connect_timeout' => 5.0,
|
||||
// 更多配置项请参考 [Guzzle](https://guzzle-cn.readthedocs.io/zh_CN/latest/request-options.html)
|
||||
],
|
||||
// 'mode' => 'dev', // optional,设置此参数,将进入沙箱模式
|
||||
];
|
||||
}
|
||||
|
||||
//支付宝支付实例 单例模式
|
||||
public static function getInstance(): ?\Yansongda\Pay\Gateways\Alipay
|
||||
{
|
||||
if (self::$app == null) {
|
||||
self::$app = Pay::alipay(self::config());
|
||||
}
|
||||
return self::$app;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,204 @@
|
|||
<?php
|
||||
namespace app\service;
|
||||
|
||||
use think\Paginator;
|
||||
//自定义分页驱动
|
||||
|
||||
class DxtcPage extends Paginator
|
||||
{
|
||||
|
||||
protected $linkStr;
|
||||
/**
|
||||
* 上一页按钮
|
||||
* @param string $text
|
||||
* @return string
|
||||
*/
|
||||
protected function getPreviousButton(string $text = "<"): string
|
||||
{
|
||||
|
||||
if ($this->currentPage() <= 1) {
|
||||
return ('<a href="javascript:;">上一页</a>');
|
||||
}
|
||||
|
||||
$url = $this->url(
|
||||
$this->currentPage() - 1
|
||||
);
|
||||
|
||||
// return ($this->getPageLinkWrapper($url, "上一页" ,"") .$this->getPageLinkWrapper($url, $text ,"prev"));
|
||||
return ($this->getPageLinkWrapper($url, "上一页" ,"prev"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 下一页按钮
|
||||
* @param string $text
|
||||
* @return string
|
||||
*/
|
||||
protected function getNextButton(string $text = '>'): string
|
||||
{
|
||||
if (!$this->hasMore) {
|
||||
//return ('<div class="li"><a href="javascript:;" class="prev">下一页</a></div>');
|
||||
return ('<a href="">下一页</a>');
|
||||
}
|
||||
|
||||
$url = $this->url($this->currentPage() + 1);
|
||||
|
||||
return ($this->getPageLinkWrapper($url, "下一页",'next')) ;
|
||||
}
|
||||
|
||||
/**
|
||||
* 页码按钮
|
||||
* @return string
|
||||
*/
|
||||
protected function getLinks(): string
|
||||
{
|
||||
if ($this->simple) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$block = [
|
||||
'first' => null,
|
||||
'slider' => null,
|
||||
'last' => null,
|
||||
];
|
||||
|
||||
$side = 3;
|
||||
$window = $side * 1;
|
||||
|
||||
if ($this->lastPage < $window + 3) {
|
||||
$block['first'] = $this->getUrlRange(1, $this->lastPage);
|
||||
} elseif ($this->currentPage <= $window) {
|
||||
$block['first'] = $this->getUrlRange(1, $window );
|
||||
$block['last'] = $this->getUrlRange($this->lastPage - 1, $this->lastPage);
|
||||
} elseif ($this->currentPage > ($this->lastPage - $window)) {
|
||||
$block['first'] = $this->getUrlRange(1, 2);
|
||||
$block['last'] = $this->getUrlRange($this->lastPage - ($window ), $this->lastPage);
|
||||
} else {
|
||||
$block['first'] = $this->getUrlRange(1, 2);
|
||||
$block['slider'] = $this->getUrlRange($this->currentPage , $this->currentPage );
|
||||
$block['last'] = $this->getUrlRange($this->lastPage - 1, $this->lastPage);
|
||||
}
|
||||
|
||||
$html = '';
|
||||
|
||||
if (is_array($block['first'])) {
|
||||
$html .= $this->getUrlLinks($block['first']);
|
||||
}
|
||||
|
||||
if (is_array($block['slider'])) {
|
||||
$html .= $this->getDots();
|
||||
$html .= $this->getUrlLinks($block['slider']);
|
||||
}
|
||||
|
||||
if (is_array($block['last'])) {
|
||||
$html .= $this->getDots();
|
||||
$html .= $this->getUrlLinks($block['last']);
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染分页html
|
||||
* @return mixed
|
||||
*/
|
||||
public function render($linkStr='')
|
||||
{
|
||||
$this->linkStr=$linkStr;
|
||||
if ($this->hasPages()) {
|
||||
if ($this->simple) {
|
||||
return sprintf(
|
||||
'<div class=" pase-size">%s %s</div>',
|
||||
$this->getPreviousButton(),
|
||||
$this->getNextButton()
|
||||
);
|
||||
} else {
|
||||
return sprintf(
|
||||
'<div class="pase-size" >%s %s %s</div>',
|
||||
$this->getPreviousButton(),
|
||||
$this->getLinks(),
|
||||
$this->getNextButton()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成一个可点击的按钮
|
||||
*
|
||||
* @param string $url
|
||||
* @param string $page
|
||||
* @return string
|
||||
*/
|
||||
protected function getAvailablePageWrapper(string $url, string $page,$class=""): string
|
||||
{
|
||||
//return '<li class="li" ><a href="' . htmlentities($url) . $this->linkStr.'" class="'.$class.'">' . $page . '</a></li>';
|
||||
return '<a href="' . htmlentities($url) . $this->linkStr.'" class="'.$class.'">' . $page . '</a>';
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成一个禁用的按钮
|
||||
*
|
||||
* @param string $text
|
||||
* @return string
|
||||
*/
|
||||
protected function getDisabledTextWrapper(string $text,$class=""): string
|
||||
{
|
||||
//return '<li class="li disabled" ><a class="'.$class.'">' . $text . '</a></li> ';
|
||||
return '<a class="'.$class.' disabled">' . $text . '</a>';
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成一个激活的按钮
|
||||
*
|
||||
* @param string $text
|
||||
* @return string
|
||||
*/
|
||||
protected function getActivePageWrapper(string $text,$class=""): string
|
||||
{
|
||||
//return '<li class="li"><a class="'.$class.'">' . $text . '</a></li>';
|
||||
return '<a class="'.$class.'">' . $text . '</a>';
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成省略号按钮
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getDots(): string
|
||||
{
|
||||
return $this->getDisabledTextWrapper('...');
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量生成页码按钮.
|
||||
*
|
||||
* @param array $urls
|
||||
* @return string
|
||||
*/
|
||||
protected function getUrlLinks(array $urls): string
|
||||
{
|
||||
$html = '';
|
||||
|
||||
foreach ($urls as $page => $url) {
|
||||
$html .= $this->getPageLinkWrapper($url, $page);
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成普通页码按钮
|
||||
*
|
||||
* @param string $url
|
||||
* @param string $page
|
||||
* @return string
|
||||
*/
|
||||
protected function getPageLinkWrapper(string $url, string $page ,$class=""): string
|
||||
{
|
||||
if ($this->currentPage() == $page) {
|
||||
return $this->getActivePageWrapper($page,'active');
|
||||
}
|
||||
|
||||
return $this->getAvailablePageWrapper($url, $page,$class);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,201 @@
|
|||
<?php
|
||||
namespace app\service;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Shared\Date as EDate;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Alignment;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Border;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
|
||||
use think\file\UploadedFile;
|
||||
|
||||
class Excel
|
||||
{
|
||||
// 导出excel默认样式
|
||||
public static array $excelStyle = [
|
||||
'font' => [
|
||||
'name' => '宋体',
|
||||
],
|
||||
'alignment' => [
|
||||
'horizontal' => Alignment::HORIZONTAL_CENTER, // 水平居中
|
||||
'vertical' => Alignment::VERTICAL_CENTER, // 垂直居中
|
||||
'wrapText' => true,
|
||||
],
|
||||
'borders' => [
|
||||
'allBorders' => [
|
||||
'borderStyle' => Border::BORDER_THIN,
|
||||
'color' => ['rgb' => 'eeeeee'],
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
public static array $defaultSetting = [
|
||||
'cell_width' => 30, // 默认列宽
|
||||
'font_size' => 12, // 默认excel内容字体大小
|
||||
];
|
||||
|
||||
//导出
|
||||
static public function export($spreadsheet,$filename)
|
||||
{
|
||||
$writer = new Xlsx($spreadsheet);
|
||||
header("Content-Type: application/force-download");
|
||||
header("Content-Type: application/octet-stream");
|
||||
header("Content-Type: application/download");
|
||||
header('Content-Disposition:inline;filename="'.$filename.'"');
|
||||
header("Content-Transfer-Encoding: binary");
|
||||
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
|
||||
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
|
||||
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
|
||||
header("Pragma: no-cache");
|
||||
$writer->save('php://output');
|
||||
$spreadsheet->disconnectWorksheets();
|
||||
unset($spreadsheet);
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function cancelTimeLimit()
|
||||
{
|
||||
ini_set('max_execution_time', '0');
|
||||
ini_set("memory_limit", '-1');
|
||||
set_time_limit(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据header字段获取可配置列的cellId列表
|
||||
* 默认前26列为可配置列A~Z, $headerLength不能超过26 * 27 = 702
|
||||
*
|
||||
* @param int $headerLength
|
||||
* @return array
|
||||
*/
|
||||
public static function getCellIds(int $headerLength): array
|
||||
{
|
||||
$defaultCellIds = range('A', 'Z', 1);
|
||||
$cellIds = $defaultCellIds;
|
||||
$loop = ceil($headerLength / 26);
|
||||
if($loop>1) {
|
||||
$maxPrefixIndex = ($loop - 2) >= 25 ? 25 : ($loop - 2);
|
||||
for ($prefixIndex = 0; $prefixIndex<= $maxPrefixIndex; $prefixIndex++) {
|
||||
$prefix = $defaultCellIds[$prefixIndex];
|
||||
$cellIds = array_merge($cellIds, array_map(function ($val) use($prefix) {
|
||||
return $prefix.$val;
|
||||
}, $defaultCellIds));
|
||||
}
|
||||
}
|
||||
return $cellIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置导出表数据
|
||||
*
|
||||
* @param Worksheet $sheet 工作表对象
|
||||
* @param array $cellValues 数据信息,二维数组(ps:若字段值需要指定样式以数组格式传递,如[$val, DataType::TYPE_STRING])。第二行开始填充
|
||||
* @param array $header 表头信息, 第一行为表头
|
||||
* @param string $sheetTitle 工作表标题
|
||||
* @param array $cellWidthList 列宽样式,键值对,键名为列序号(从0开始计算)
|
||||
* @param array $excelStyle 表数据样式
|
||||
* @param array $defaultList 默认设置样式
|
||||
* @param array $cellWrapList 列换行样式
|
||||
* @return bool
|
||||
*/
|
||||
public static function setExcelCells(Worksheet $sheet, array $cellValues, array $header, string $sheetTitle = '数据列表', $cellWidthList = [], $excelStyle = [], $defaultList = [], $cellWrapList = []): bool
|
||||
{
|
||||
$defaultStyle = [
|
||||
'font' => [
|
||||
'name' => '宋体',
|
||||
],
|
||||
'alignment' => [
|
||||
'horizontal' => Alignment::HORIZONTAL_CENTER, // 水平居中
|
||||
'vertical' => Alignment::VERTICAL_CENTER, // 垂直居中
|
||||
'wrapText' => false,
|
||||
],
|
||||
'borders' => [
|
||||
'allBorders' => [
|
||||
'borderStyle' => Border::BORDER_THIN,
|
||||
'color' => ['rgb'=>'eeeeee'],
|
||||
]
|
||||
],
|
||||
];
|
||||
$headerLength = count($header);
|
||||
|
||||
if($headerLength === 0) return false;
|
||||
$cellIds = self::getCellIds($headerLength);
|
||||
$lastCellId = $cellIds[$headerLength-1];
|
||||
$contentIndex = 2;
|
||||
foreach ($cellValues as $n => $itemCell) {
|
||||
foreach ($itemCell as $i => $cellValue) {
|
||||
if(is_array($cellValue)) {
|
||||
$sheet->setCellValueExplicit($cellIds[$i] . $contentIndex, ...$cellValue);
|
||||
} else {
|
||||
$sheet->setCellValue($cellIds[$i] . $contentIndex, $cellValue);
|
||||
}
|
||||
}
|
||||
$contentIndex++;
|
||||
}
|
||||
|
||||
try {
|
||||
$headerStr = 'A1:' . $lastCellId.'1';
|
||||
$bodyStr = 'A2:' . $lastCellId . ($contentIndex - 1);
|
||||
|
||||
$defaultWidth = 24; // 默认列宽24个字符
|
||||
$fontSize = 12; // 默认字体大小为12号
|
||||
if(!empty($defaultList)) {
|
||||
if(isset($defaultList['cell_width']) && is_numeric($defaultList['cell_width']) && $defaultList['cell_width'] > 0) {
|
||||
$defaultWidth = $defaultList['cell_width'];
|
||||
}
|
||||
if(isset($defaultList['font_size']) && is_numeric($defaultList['font_size']) && $defaultList['font_size'] > 0) {
|
||||
$fontSize = $defaultList['font_size'];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$sheet->setTitle(empty($sheetTitle) ? '数据列表': $sheetTitle);
|
||||
$sheet->getDefaultColumnDimension()->setAutoSize($fontSize);
|
||||
$sheet->getStyle($headerStr)->getFont()->setBold(false)->setSize($fontSize+2);
|
||||
$sheet->getDefaultColumnDimension()->setWidth($defaultWidth);
|
||||
$sheet->fromArray($header, null, 'A1');
|
||||
$sheet->getStyle($headerStr)->applyFromArray($defaultStyle);
|
||||
$sheet->getStyle($bodyStr)->applyFromArray(empty($excelStyle) ? $defaultStyle : $excelStyle);
|
||||
|
||||
// 自定义列宽
|
||||
if(!empty($cellWidthList)) {
|
||||
foreach ($cellWidthList as $cellId => $widthVal) {
|
||||
if(isset($cellIds[$cellId]) && is_numeric($widthVal) && $widthVal > 0) {
|
||||
$sheet->getColumnDimension($cellIds[$cellId])->setWidth($widthVal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//自定义列是否换行
|
||||
if(!empty($cellWrapList)) {
|
||||
foreach ($cellWrapList as $cellId => $boolVal) {
|
||||
if(isset($cellIds[$cellId])) {
|
||||
$wrap = $boolVal ? true : false;
|
||||
$sheet->getStyle($cellIds[$cellId])->getAlignment()->setWrapText($wrap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// excel导入时间转换
|
||||
public static function getExcelTime($timeStr, $format = 'Y-m-d')
|
||||
{
|
||||
$timezone = ini_get('date.timezone');
|
||||
$timeStr = trim($timeStr);
|
||||
if (!empty($timeStr)) {
|
||||
if (is_numeric($timeStr)) {
|
||||
$toTimestamp = EDate::excelToTimestamp($timeStr, $timezone);
|
||||
$timeStr = date($format, $toTimestamp);
|
||||
} else {
|
||||
$toTimestamp = strtotime($timeStr);
|
||||
$timeStr = ($toTimestamp === false) ? '' : date($format, $toTimestamp);
|
||||
}
|
||||
} else {
|
||||
$timeStr = '';
|
||||
}
|
||||
return $timeStr;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
namespace app\service;
|
||||
|
||||
use think\facade\Config as CConfig;
|
||||
|
||||
/**
|
||||
* 扩展配置项工具类
|
||||
* Class ExtraConfig
|
||||
* @package app\service
|
||||
*/
|
||||
class ExtraConfig
|
||||
{
|
||||
private static string $extraDir = 'extra/';
|
||||
|
||||
/**
|
||||
* 比率设置
|
||||
* @return array|mixed
|
||||
*/
|
||||
public static function ratio()
|
||||
{
|
||||
CConfig::load(self::$extraDir.'ratio', 'ratio');
|
||||
return config('ratio') ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信相关设置
|
||||
* @return array|mixed
|
||||
*/
|
||||
public static function wechat()
|
||||
{
|
||||
CConfig::load(self::$extraDir.'wechat', 'wechat');
|
||||
return config('wechat') ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 小程序个性装修配置
|
||||
* @return array|mixed
|
||||
*/
|
||||
public static function miniProgram()
|
||||
{
|
||||
CConfig::load('extra/mini_program', 'mini_program');
|
||||
return config('mini_program') ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 基础配置
|
||||
*/
|
||||
public static function base()
|
||||
{
|
||||
CConfig::load('extra/base', 'base');
|
||||
return config('base') ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 阿里OSS配置
|
||||
*/
|
||||
public static function aliOss()
|
||||
{
|
||||
CConfig::load('extra/alioss', 'alioss');
|
||||
return config('alioss') ?? [];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
namespace app\service;
|
||||
|
||||
use think\file\UploadedFile;
|
||||
|
||||
class File
|
||||
{
|
||||
//上传文件移动到上传文件夹
|
||||
public static function move(UploadedFile $file)
|
||||
{
|
||||
$upload_path = 'storage/uploads/' . date('Ymd');
|
||||
$path = app()->getRootPath() . $upload_path;
|
||||
$filename = uniqid() . '.' . $file->extension();
|
||||
$upload_filename = '/' . $upload_path . '/' . $filename;
|
||||
return [$file->move($path, $filename), $file, $upload_filename];
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件访问路径转换为完整的url
|
||||
* @param string|null $fileUrl
|
||||
* @param bool $ossAnalysis 是否进行OSS解析
|
||||
* @return string
|
||||
* @todo 若启用OOS存储,需根据业务配置调整$fileDomain
|
||||
*
|
||||
*/
|
||||
public static function convertCompleteFileUrl(?string $fileUrl, bool $ossAnalysis=true): string
|
||||
{
|
||||
if (empty($fileUrl)) {
|
||||
return '';
|
||||
}
|
||||
if ($ossAnalysis) {
|
||||
$fileDomain = self::getFileDomain();
|
||||
} else {
|
||||
$fileDomain = request()->domain();
|
||||
}
|
||||
$prefix = substr($fileUrl, 0, 4);
|
||||
if (!($prefix == 'http')) {
|
||||
$fileUrl = $fileDomain.'/'.ltrim($fileUrl, '/');
|
||||
}
|
||||
|
||||
return $fileUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件访问域名前缀
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getFileDomain(): string
|
||||
{
|
||||
$confBase = ExtraConfig::base();
|
||||
$confOss = ExtraConfig::aliOss();
|
||||
$isOss = $confBase['oss'] ?? 'false';
|
||||
$ossDomain = $confOss['customDomain'] ?? '';
|
||||
|
||||
// 默认为当前域名
|
||||
$fileDomain = request()->domain();
|
||||
if ($isOss == 'true' && !empty($ossDomain)) {
|
||||
$fileDomain = $ossDomain;
|
||||
}
|
||||
|
||||
$fileDomain = trim($fileDomain);
|
||||
return rtrim($fileDomain, '/');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
<?php
|
||||
namespace app\service;
|
||||
|
||||
use think\Exception;
|
||||
|
||||
/**
|
||||
* GD2库绘画
|
||||
* Class GdTool
|
||||
* @package app\service
|
||||
*/
|
||||
class GdTool
|
||||
{
|
||||
|
||||
/**
|
||||
* 生成海报
|
||||
* 背景尺寸固定: 750 * 1334
|
||||
*
|
||||
* @param string $srcQr 生成的二维码base64值
|
||||
* @param string $bgImg 背景图片 自定义背景图
|
||||
* @param string $savePath 海报保存路径,为空则返回base64值
|
||||
* @return bool|string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function generatePoster(string $srcQr='', string $bgImg = '', string $savePath = '')
|
||||
{
|
||||
try {
|
||||
if (!empty($savePath)) {
|
||||
if (!is_dir($savePath)) {
|
||||
@mkdir($savePath, 0777, true);
|
||||
}
|
||||
if (!is_dir($savePath) || !is_writeable($savePath)) {
|
||||
throw new \Exception('无法保存');
|
||||
}
|
||||
}
|
||||
|
||||
//1、创建画布资源
|
||||
// 背景尺寸: 750 * 1334
|
||||
$defBga = app()->getRootPath().'public/static/images/poster-bg1.png';
|
||||
$srcBga = empty($bgImg) ? $defBga : $bgImg;
|
||||
$bgInfo = @getimagesize($srcBga);
|
||||
if ($bgInfo[0] != 750 || $bgInfo[1] != 1334) {
|
||||
throw new \Exception('海报模板尺寸不正确!');
|
||||
}
|
||||
|
||||
if (!$bgInfo) {
|
||||
throw new \Exception('海报背景图资源不存在!');
|
||||
}
|
||||
|
||||
$ext = $bgInfo['mime'];
|
||||
$extName = '';
|
||||
$img = null;
|
||||
switch ($ext) {
|
||||
case 'image/jpeg':
|
||||
$img = @imagecreatefromjpeg($srcBga);
|
||||
$extName = 'jpg';
|
||||
break;
|
||||
case 'image/png':
|
||||
$img = @imagecreatefrompng($srcBga);
|
||||
$extName = 'png';
|
||||
break;
|
||||
}
|
||||
if (!$img) {
|
||||
throw new \Exception('无效背景图');
|
||||
}
|
||||
|
||||
//2、准备颜色
|
||||
$black = imagecolorallocate($img,0,0,0);
|
||||
$while = imagecolorallocate($img,255,255,255);
|
||||
$faColor = imagecolorallocate($img,0,104,51);
|
||||
|
||||
// 邀请人头像
|
||||
// $headimgurl = 'https://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTJXE3Zz0U5edXYI2icicYibSNwwezWe0X92fovRtpUwdCF5lAmjsYK5EWT3R8ItO0BEqynElYhWibRqDg/132';
|
||||
// $headimg = imagecreatefromstring(file_get_contents($headimgurl));
|
||||
// imagecopymerge($img, $headimg, 50, 1000, 0, 0, 120, 120, 100);
|
||||
// imagecopyresampled($img, $headimg, 90, 900, 0, 0, 120, 120, 120, 120);
|
||||
|
||||
// 添加文字
|
||||
// imagettftext($img, 18, 0, 220, 1100, 250, public_path().'static/simheittf.ttf', '超级凉面...邀您关注');
|
||||
|
||||
//填充画布(背景色)
|
||||
imagefill($img,0,0, $while);
|
||||
|
||||
// 组合二维码图片 ,坐标:x:246; y:959
|
||||
$prefixPng = 'data:image/png;base64,';
|
||||
$qrStr = base64_decode(str_replace($prefixPng,"",$srcQr));
|
||||
$qrImg = @imagecreatefromstring($qrStr);
|
||||
list($qrWidth, $qrHeight) = getimagesize($srcQr);
|
||||
if(!$qrImg) {
|
||||
imagedestroy($img);
|
||||
throw new \Exception('无效二维码');
|
||||
}
|
||||
$imgQrW = $imgQrH = 274;
|
||||
imagecopyresampled($img, $qrImg, 456, 959, 0, 0, $imgQrW, $imgQrH, $qrWidth, $qrHeight);
|
||||
|
||||
imagedestroy($qrImg);
|
||||
//4、输出与保存最终图像(保存文件或返回base64)
|
||||
|
||||
|
||||
if (empty($savePath)) {
|
||||
/* 返回base64 */
|
||||
ob_start();
|
||||
if ($ext == 'image/jpeg') {
|
||||
imagejpeg($img);
|
||||
} else {
|
||||
imagepng($img);
|
||||
}
|
||||
|
||||
$imgData = ob_get_contents();
|
||||
ob_end_clean();
|
||||
imagedestroy($img);
|
||||
|
||||
$prefix = 'data:image/jpg/png/gif;base64,';
|
||||
return $prefix.base64_encode($imgData);
|
||||
|
||||
} else {
|
||||
/* 保存到文件*/
|
||||
$fileName = md5(microtime(true)).'.'.$extName;
|
||||
$saveFile = $savePath."/".$fileName;
|
||||
if ($ext == 'image/jpeg') {
|
||||
imagejpeg($img, $saveFile);
|
||||
} else {
|
||||
imagepng($img, $saveFile);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 输出显示
|
||||
header("content-type: image/png");
|
||||
imagepng($img);
|
||||
*/
|
||||
|
||||
//5、释放画布资源
|
||||
imagedestroy($img);
|
||||
} catch (\Exception $e) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
<?php
|
||||
namespace app\service;
|
||||
|
||||
use think\Image as TImage;
|
||||
use app\model\System;
|
||||
|
||||
class Image extends File
|
||||
{
|
||||
/**
|
||||
* 对图片进行重置大小,并对宽度大于max宽度的等比缩放为宽度为1920
|
||||
* milo
|
||||
* 2019-10-24修改
|
||||
*/
|
||||
public static function resize($src)
|
||||
{
|
||||
$max = 1920;
|
||||
$realPath = app()->getRootPath() . 'public/' . ltrim($src,'/');
|
||||
if(is_file($realPath)){
|
||||
$img = TImage::open($realPath);
|
||||
list($img_w,$img_h) = $img->size();
|
||||
if($max > 0 && $img_w > $max){
|
||||
$img->thumb($max, $max * ($img_h / $img_w))->save($realPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加水印
|
||||
* milo
|
||||
* 2018-01-17
|
||||
*/
|
||||
public static function mark($src)
|
||||
{
|
||||
$rootPath = app()->getRootPath();
|
||||
if(!empty($src)){
|
||||
$system = System::getSystem();
|
||||
$realPath = $rootPath . 'public/' . ltrim($src, '/');
|
||||
if(is_file($realPath)){
|
||||
if($system['is_mark']){
|
||||
$mark = $rootPath . ltrim($system['mark_img'], '/');
|
||||
if(is_file($mark)){
|
||||
$mark_position = $system['mark_position']??5;
|
||||
$mark_opacity = $system['mark_opacity']??50;
|
||||
$img = TImage::Open($realPath);
|
||||
$img->water($mark,$mark_position,$mark_opacity)->save($realPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//获取水印位置键值对
|
||||
public static function getMarkPosition()
|
||||
{
|
||||
return [
|
||||
"1" => '上左',
|
||||
"2" => '上中',
|
||||
"3" => '上右',
|
||||
"4" => '中左',
|
||||
"5" => '正中',
|
||||
"6" => '中右',
|
||||
"7" => '下左',
|
||||
"8" => '下中',
|
||||
"9" => '下右'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除图片
|
||||
* milo
|
||||
* 2018-01-15
|
||||
*/
|
||||
public static function delImg($src)
|
||||
{
|
||||
if(!empty(trim($src))){
|
||||
$realPath = app()->getRootPath() . 'public/' . ltrim($src, '/');
|
||||
if (file_exists($realPath)) {
|
||||
$info = pathinfo($realPath);
|
||||
$source = $info['dirname'] . "/" . $info['filename'] . '*.' . $info['extension'];
|
||||
foreach(glob($source) as $filename){
|
||||
if(is_file($filename)){
|
||||
unlink($filename);
|
||||
}
|
||||
}
|
||||
clearstatcache();// 清除缓存
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 获取缩略图
|
||||
* milo
|
||||
* 2019-10-24修改
|
||||
* 避免跨平台出错,目录分隔符全部转换为'/'
|
||||
* app()->getRuntimePath() = app()->getRootPath().'runtime/当前应用模块(api)/'
|
||||
*/
|
||||
public static function getThumb($src,$width=0,$height=0,$type = TImage::THUMB_CENTER)
|
||||
{
|
||||
if(empty($src)){
|
||||
return '';
|
||||
}
|
||||
$rootPath = app()->getRootPath();
|
||||
$realPath = $rootPath . 'public/' . ltrim($src, '/');
|
||||
$realPath = str_replace('\\', '/', $realPath);
|
||||
if(!file_exists($realPath)){
|
||||
return '';
|
||||
}
|
||||
$info = pathinfo($src);
|
||||
if($width <= 0 && $height <= 0){ //高宽都小于或等于0,则返回原图片
|
||||
return $src;
|
||||
}
|
||||
$image = TImage::open($realPath);
|
||||
list($imageWidth, $imageHeight) = $image->size();
|
||||
if($width <= 0){
|
||||
$width = floor($height * ($imageWidth / $imageHeight));
|
||||
}elseif($height <= 0){
|
||||
$height = floor($width * ($imageHeight / $imageWidth));
|
||||
}
|
||||
if($width >= $imageWidth || $height >= $imageHeight){
|
||||
return $src;
|
||||
}
|
||||
$thumbName = $info['dirname']. "/" .$info['filename'].'_'.$width.'_'.$height.'.'.$info['extension'];
|
||||
$realThumbName = $rootPath . 'public/' . ltrim($thumbName, '/');
|
||||
$realThumbName = str_replace('\\', '/', $realThumbName);
|
||||
if(!file_exists($realThumbName)){
|
||||
$image->thumb($width, $height, $type)->save($realThumbName);
|
||||
}
|
||||
return str_replace('\\', '/', $thumbName);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
<?php
|
||||
|
||||
namespace app\service;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use DateTimeZone;
|
||||
use Exception;
|
||||
use Lcobucci\Clock\SystemClock;
|
||||
use Lcobucci\JWT\Configuration;
|
||||
use Lcobucci\JWT\Signer\Hmac\Sha256;
|
||||
use Lcobucci\JWT\Signer\Key\InMemory;
|
||||
use Lcobucci\JWT\UnencryptedToken;
|
||||
use Lcobucci\JWT\Validation\Constraint\IssuedBy;
|
||||
use Lcobucci\JWT\Validation\Constraint\LooseValidAt;
|
||||
use Lcobucci\JWT\Validation\Constraint\PermittedFor;
|
||||
use Lcobucci\JWT\Validation\Constraint\ValidAt;
|
||||
|
||||
class Jwt
|
||||
{
|
||||
private static $secret = 'lF9XkOMfpsR0ODVfbasY2HtDrIps8GIX';
|
||||
private static $expire = 7200 * 10;//秒
|
||||
private static $iss = 'dxtc';//jwt签发者
|
||||
private static $sub = 'dxtc-customer';//jwt所面向的用户
|
||||
private static $aud = 'dxtc-customer';//接受jwt的一方
|
||||
|
||||
private static function config(): Configuration
|
||||
{
|
||||
return Configuration::forSymmetricSigner(
|
||||
// You may use any HMAC variations (256, 384, and 512)
|
||||
new Sha256(),
|
||||
// replace the value below with a key of your own!
|
||||
InMemory::base64Encoded(self::$secret)
|
||||
// You may also override the JOSE encoder/decoder if needed by providing extra arguments here
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取token有效期 单位秒
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function expire(): int
|
||||
{
|
||||
return self::$expire;
|
||||
}
|
||||
|
||||
/**
|
||||
* 编码
|
||||
*
|
||||
* @param $data
|
||||
* @param int $expire
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function generate($data, int $expire = 0): string
|
||||
{
|
||||
$expire = $expire <= 0 ? self::$expire : $expire;
|
||||
$now = new DateTimeImmutable('now', new DateTimeZone('Asia/ShangHai'));
|
||||
|
||||
$token = self::config()->builder()
|
||||
// Configures the issuer (iss claim)
|
||||
->issuedBy(self::$iss)
|
||||
// Configures the audience (aud claim)
|
||||
->permittedFor(self::$aud)
|
||||
// Configures the id (jti claim)
|
||||
// ->identifiedBy($this->jti)
|
||||
// Configures the time that the token was issue (iat claim)
|
||||
->issuedAt($now)
|
||||
// Configures the expiration time of the token (exp claim)
|
||||
->expiresAt($now->modify(sprintf('+%d seconds', $expire)))
|
||||
// Configures a new claim, called "uid"
|
||||
->withClaim('data', $data)
|
||||
// Configures a new header, called "foo"
|
||||
// ->withHeader('foo', 'bar')
|
||||
// Builds a new token
|
||||
->getToken(self::config()->signer(), self::config()->signingKey());
|
||||
|
||||
return $token->toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析
|
||||
*
|
||||
* @param string $tokenStr
|
||||
* @return array|mixed
|
||||
*/
|
||||
public static function parse(string $tokenStr)
|
||||
{
|
||||
$config = self::config();
|
||||
|
||||
try {
|
||||
$token = $config->parser()->parse($tokenStr);
|
||||
assert($token instanceof UnencryptedToken);
|
||||
return $token->claims()->all()['data'] ?? [];
|
||||
} catch (Exception $e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证token
|
||||
*
|
||||
* @param string $tokenStr
|
||||
* @return bool
|
||||
*/
|
||||
public static function validate(string $tokenStr): bool
|
||||
{
|
||||
$config = self::config();
|
||||
try {
|
||||
$token = $config->parser()->parse($tokenStr);
|
||||
assert($token instanceof UnencryptedToken);
|
||||
|
||||
//验证签发人iss是否正确
|
||||
$validateIssued = new IssuedBy(self::$iss);
|
||||
$config->setValidationConstraints($validateIssued);
|
||||
//验证客户端aud是否匹配
|
||||
$validateAud = new PermittedFor(self::$aud);
|
||||
$config->setValidationConstraints($validateAud);
|
||||
|
||||
//验证是否过期 exp
|
||||
$timezone = new DateTimeZone('Asia/Shanghai');
|
||||
$now = new SystemClock($timezone);
|
||||
$validateExpired = new LooseValidAt($now);
|
||||
$config->setValidationConstraints($validateExpired);
|
||||
|
||||
$constraints = $config->validationConstraints();
|
||||
|
||||
return $config->validator()->validate($token, ...$constraints);
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
|
||||
namespace app\service;
|
||||
|
||||
class Kd100
|
||||
{
|
||||
private static $url = 'http://poll.kuaidi100.com/poll/query.do'; //实时查询请求地址
|
||||
|
||||
private static $key = 'bRIlFitN9883';//客户授权key
|
||||
private static $customer = '03A67821933221AAE6CBACBCA7E565F2';//查询公司编号
|
||||
|
||||
public static function query(string $com, string $num, $returnArray = false)
|
||||
{
|
||||
$param = [
|
||||
'com' => $com, //快递公司编码, 一律用小写字母
|
||||
'num' => $num, //快递单号
|
||||
// 'phone' => '', //手机号
|
||||
// 'from' => '', //出发地城市
|
||||
// 'to' => '', //目的地城市
|
||||
// 'resultv2' => '1' //开启行政区域解析
|
||||
];
|
||||
|
||||
//请求参数
|
||||
$post_data = [];
|
||||
$post_data["customer"] = self::$customer;
|
||||
$post_data["param"] = json_encode($param);
|
||||
$sign = md5($post_data["param"].self::$key.$post_data["customer"]);
|
||||
$post_data["sign"] = strtoupper($sign);
|
||||
|
||||
$params = "";
|
||||
foreach ($post_data as $k => $v) {
|
||||
$params .= "$k=".urlencode($v)."&"; //默认UTF-8编码格式
|
||||
}
|
||||
$post_data = substr($params, 0, -1);
|
||||
|
||||
//发送post请求
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_POST, 1);
|
||||
curl_setopt($ch, CURLOPT_HEADER, 0);
|
||||
curl_setopt($ch, CURLOPT_URL, self::$url);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
$result = curl_exec($ch);
|
||||
return json_decode($result, $returnArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public static function state(): array
|
||||
{
|
||||
return [
|
||||
0 => '在途',
|
||||
1 => '揽收',
|
||||
2 => '疑难',
|
||||
3 => '签收',
|
||||
4 => '退签',
|
||||
5 => '派件',
|
||||
6 => '退回',
|
||||
7 => '转单',
|
||||
10 => '待清关',
|
||||
11 => '清关中',
|
||||
12 => '已清关',
|
||||
13 => '清关异常',
|
||||
14 => '收件人拒签',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 不在继续查询的配送状态
|
||||
* @return int[]
|
||||
*/
|
||||
public static function unSearchState(): array
|
||||
{
|
||||
return [3,4,12];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
namespace app\service;
|
||||
|
||||
/**
|
||||
* 精准计算 依赖bc库
|
||||
*
|
||||
* Class Math
|
||||
* @package app\service
|
||||
*/
|
||||
class Math
|
||||
{
|
||||
// 加法
|
||||
public static function add(string $num1, string $num2, $scale = 2): string
|
||||
{
|
||||
return bcadd($num1, $num2, $scale);
|
||||
}
|
||||
|
||||
// 减法
|
||||
public static function sub(string $num1, string $num2, $scale = 2): string
|
||||
{
|
||||
return bcsub($num1, $num2, $scale);
|
||||
}
|
||||
|
||||
// 乘法
|
||||
public static function mul(string $num1, string $num2, $scale = 2): string
|
||||
{
|
||||
return bcmul($num1, $num2, $scale);
|
||||
}
|
||||
|
||||
// 除法
|
||||
public static function div(string $num1, string $num2, $scale = 2): string
|
||||
{
|
||||
return bcdiv($num1, $num2, $scale);
|
||||
}
|
||||
|
||||
// 求余、取模
|
||||
public static function mod(string $num1, string $num2, $scale = 2): string
|
||||
{
|
||||
return bcmod($num1, $num2, $scale);
|
||||
}
|
||||
|
||||
// 分转元
|
||||
public static function fen2Yuan(int $amount): string
|
||||
{
|
||||
return self::div($amount, '100');
|
||||
}
|
||||
|
||||
// 元转分
|
||||
public static function yuan2Fen(string $amount, int $scale = 0): string
|
||||
{
|
||||
return self::mul($amount, '100', $scale);
|
||||
}
|
||||
|
||||
// 格式化 默认保留小数点后两位
|
||||
public static function format($amount, int $scale = 2): string
|
||||
{
|
||||
return number_format($amount, $scale, '.', '');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,306 @@
|
|||
<?php
|
||||
|
||||
namespace app\service;
|
||||
|
||||
use Exception;
|
||||
use think\db\exception\DataNotFoundException;
|
||||
use think\db\exception\DbException;
|
||||
use think\db\exception\ModelNotFoundException;
|
||||
use think\Model;
|
||||
use think\facade\Db;
|
||||
use think\Collection;
|
||||
use think\facade\Env;
|
||||
use think\facade\Log;
|
||||
use think\Paginator;
|
||||
use app\exception\RepositoryException;
|
||||
|
||||
/**
|
||||
* 领域模型-仓储
|
||||
*
|
||||
* Class Repository
|
||||
* @package app\service
|
||||
*/
|
||||
class Repository
|
||||
{
|
||||
/**
|
||||
* @var array 已加载对象列表
|
||||
*/
|
||||
private static $objects = [];
|
||||
|
||||
/**
|
||||
* @var Model 模型对象
|
||||
*/
|
||||
protected $model;
|
||||
|
||||
// 布尔值数字关系
|
||||
public const BOOL_FALSE = 0;
|
||||
public const BOOL_TRUE = 1;
|
||||
|
||||
/**
|
||||
* 获取当前子对象实列(单例形式返回)
|
||||
*
|
||||
* @param Model|null $model 模型对象。未指定则自动获取
|
||||
* @return mixed
|
||||
*/
|
||||
public static function getInstance(Model $model = null)
|
||||
{
|
||||
$class = get_called_class();
|
||||
|
||||
if (isset(self::$objects[$class]) && self::$objects[$class] !== null) {
|
||||
return self::$objects[$class];
|
||||
}
|
||||
|
||||
$obj = new $class();
|
||||
if ($model) {
|
||||
$obj->model = $model;
|
||||
} elseif (strpos($class, '\\repository') > 0) {
|
||||
$model = str_replace('\\repository', '\\model', $class);
|
||||
//去掉末尾Repository app\model\AccountRepository =》 app\model\Account
|
||||
$model = substr($model, 0, strlen($model) - strlen('Repository'));
|
||||
if (class_exists($model)) {
|
||||
$obj->model = new $model;
|
||||
}
|
||||
}
|
||||
|
||||
self::$objects[$class] = $obj;
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable $callback
|
||||
* @param mixed $failReturn
|
||||
* @param bool $transaction
|
||||
* @return mixed
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
protected function access(callable $callback, $failReturn, bool $transaction = false)
|
||||
{
|
||||
$exception = null;
|
||||
try {
|
||||
if ($transaction) {
|
||||
Db::startTrans();
|
||||
}
|
||||
|
||||
$r = $callback();
|
||||
|
||||
if ($transaction) {
|
||||
Db::commit();
|
||||
}
|
||||
|
||||
if ($r) {
|
||||
return $r;
|
||||
}
|
||||
|
||||
if ($failReturn instanceof Exception) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $failReturn;
|
||||
} catch (Exception $e) {
|
||||
if ($transaction) {
|
||||
Db::rollback();
|
||||
}
|
||||
|
||||
if ($e instanceof RepositoryException) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$name = 'Domain - Repository - 未知错误';
|
||||
|
||||
$traces = $e->getTrace();
|
||||
|
||||
foreach ($traces as $i => $trace) {
|
||||
if (!empty($trace['class']) && $trace['class'] === Repository::class && $trace['function'] === 'access') {
|
||||
$trace = $traces[$i - 2] ?? null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($trace) && !empty($trace['file'][1]) && !empty($trace['line'])) {
|
||||
$parts = explode('application\\', $trace['file']);
|
||||
if (!empty($parts[1])) {
|
||||
$name = $parts[1].':'.$trace['line'];
|
||||
} else {
|
||||
$name = $trace['file'].':'.$trace['line'];
|
||||
}
|
||||
}
|
||||
|
||||
$exception = $e;
|
||||
|
||||
$line = '['.$name.'] '.$e->getMessage();
|
||||
Log::error($line);
|
||||
|
||||
throw new RepositoryException('Repository异常'.(Env::get('app_debug') ? ': '.$line : ''), 5009);
|
||||
} finally {
|
||||
if ($exception && $failReturn instanceof RepositoryException) {
|
||||
if (Env::get('app_debug')) {
|
||||
$failReturn = new RepositoryException($failReturn->getMessage().': '.$exception->getMessage(),
|
||||
$failReturn->getCode());
|
||||
}
|
||||
throw $failReturn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前model对象
|
||||
*
|
||||
* @return Model
|
||||
*/
|
||||
public function getModel(): Model
|
||||
{
|
||||
return $this->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据条件查询列表
|
||||
*
|
||||
* @param array $where 查询条件
|
||||
* @param array $fields 查询字段 []表示全部
|
||||
* @param int $page 默认第一页 0不限制
|
||||
* @param int $limit 限制条数 0不限制
|
||||
* @param callable|null $callback 更为复杂的条件 使用闭包查询
|
||||
* @return array
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
public function findList(array $where = [], array $fields = [], int $page = 1, int $limit = 0, callable $callback = null, array $order = []): ?array
|
||||
{
|
||||
$failData = [
|
||||
'total' => 0,
|
||||
'current' => $page,
|
||||
'size' => $limit,
|
||||
'list' => new Collection(),
|
||||
];
|
||||
return $this->access(function () use ($where, $fields, $page, $limit, $callback, $order) {
|
||||
return $this->model->findList($where, $fields, $page, $limit, $callback, $order);
|
||||
}, $failData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据条件查询列表[带分页 适用于后台]
|
||||
*
|
||||
* @param array $data 查询数据
|
||||
* @param array $pageParams 分页参数
|
||||
* @param callable|null $callback 复杂查询条件 使用闭包查询
|
||||
* @return Paginator
|
||||
*/
|
||||
public function findListWithPaginate(array $data = [], array $pageParams = [], callable $callback = null): Paginator
|
||||
{
|
||||
return $this->model->findListWithPaginate($data, $pageParams, $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据主键 ID 查询
|
||||
*
|
||||
* @param int $id ID
|
||||
* @param array $fields 要返回的字段,默认全部
|
||||
* @return Mixed
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
public function findById(int $id, array $fields = [], callable $callback = null)
|
||||
{
|
||||
return $this->access(function () use ($id, $fields, $callback) {
|
||||
return $this->model->findById($id, $fields, $callback);
|
||||
}, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $where
|
||||
* @param array $fields
|
||||
* @return array|Model|null
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
*/
|
||||
public function findOneByWhere(array $where, array $fields = [])
|
||||
{
|
||||
return $this->model->field($fields)->where($where)->find();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建
|
||||
*
|
||||
* @param array $data 数据
|
||||
* @return Model
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
public function create(array $data): Model
|
||||
{
|
||||
return $this->access(function () use ($data) {
|
||||
return $this->model->create($data);
|
||||
}, new RepositoryException('创建失败'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新
|
||||
*
|
||||
* @param array $data 数据
|
||||
* @param array $where 条件
|
||||
* @return bool|Exception
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
public function update(array $data, array $where): bool
|
||||
{
|
||||
return $this->access(function () use ($data, $where) {
|
||||
return $this->model->where($where)->find()->save($data);
|
||||
}, new RepositoryException('更新失败'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*
|
||||
* @param array $where 删除条件
|
||||
* @param bool $softDelete 是否软删除 默认false
|
||||
* @param string $softDeleteTime 删除时间 softDelete=true时有效
|
||||
* @param string $softDeleteField 软删除字段 softDelete=true时有效
|
||||
* @return bool
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
public function delete(array $where, bool $softDelete = false, string $softDeleteTime = '', string $softDeleteField = 'deleted_at'): bool
|
||||
{
|
||||
return $this->access(function () use ($where, $softDelete, $softDeleteField, $softDeleteTime) {
|
||||
// 注意:如果model中引入了软删除trait,$softDelete又设置false 将无法正确删除
|
||||
return $this->model->where($where)
|
||||
->when($softDelete, function ($q) use ($softDeleteField, $softDeleteTime) {
|
||||
$softDeleteTime = $softDeleteTime ?: date('Y-m-d H:i:s');
|
||||
$q->useSoftDelete($softDeleteField, $softDeleteTime);
|
||||
})->delete();
|
||||
}, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 排序
|
||||
*
|
||||
* @param int $id 排序ID
|
||||
* @param string $type 排序类型 向上、向下
|
||||
* @param int $num 移动位数
|
||||
* @param string $listType 列表的排序类型 降序|升序
|
||||
* @param array $where 额外条件 格式如:
|
||||
* $map[] = ['name','like','think'];
|
||||
* $map[] = ['status','=',1];
|
||||
* @return array
|
||||
*/
|
||||
public function sort(int $id,string $type,int $num,string $listType, array $where = []): array
|
||||
{
|
||||
return $this->model->sort($id, $type, $num, $listType, $where);
|
||||
}
|
||||
|
||||
/**
|
||||
* 日志记录
|
||||
*
|
||||
* @param string $msg
|
||||
* @param Exception|null $e
|
||||
* @param string $level
|
||||
* @param string $channel
|
||||
*/
|
||||
public static function log(string $msg, Exception $e = null, string $level = 'error', string $channel = 'file')
|
||||
{
|
||||
if ($e != null) {
|
||||
$msg = sprintf("[%s]%s:%s %s", $msg, $e->getFile(), $e->getLine(), $e->getMessage());
|
||||
} else {
|
||||
$msg = sprintf("%s", $msg);
|
||||
|
||||
}
|
||||
Log::channel($channel)->$level($msg);
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue