master
yin5th 2022-10-08 17:31:39 +08:00
commit 198397f325
2362 changed files with 379587 additions and 0 deletions

12
.gitignore vendored Executable file
View File

@ -0,0 +1,12 @@
.env
/.idea
/.vscode
.vs
backup/data/*
/public/html/*
*.log
runtime/*
public/storage/*
.DS_Store
nginx.htaccess
dump.rdb

0
.htaccess Executable file
View File

32
LICENSE.txt Executable file
View File

@ -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.

18
README.md Executable file
View File

@ -0,0 +1,18 @@
# 四川大向天诚CMS管理系统
## 部署说明
- 项目目录下是否存在`runtime`文件夹,不存在则创建,存在需要开启都写权限。
```shell
chmod -R 777 runtime
```
- public/storage文件夹同上
## PHP 扩展
- fileinfo 文件操作通用扩展
- exif 图片文件基础信息通用扩展依赖于mbstring扩展需要安装在mbstring扩展之后
## 使用技术栈
- [Thinkphp6](https://www.kancloud.cn/manual/thinkphp6_0/1037479)
- php7.1+ ~ 7.4
- mysql 5.7+
- 富文本tinymce5+

1
app/.htaccess Executable file
View File

@ -0,0 +1 @@
deny from all

497
app/common.php Executable file
View File

@ -0,0 +1,497 @@
<?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\exception\ClassNotFoundException;
use app\model\Model as ModelModel ;
// 应用公共文件
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($cate)
{
$url = '';
if(!empty($cate)){
if($cate['is_index']){
return '/';
}
if(!empty($cate['url'])){
return $cate['url'];
}
if(!empty($cate['route'])){
return $cate['route'].".html";
}
switch ($cate["model_id"]){
case ModelModel::MODEL_ARTICLE:
return url('/articles/'. $cate['id']);
break;
case ModelModel::MODEL_PAGE:
return url('/page/index', ['categoryId' => $cate['id']]);
break;
case ModelModel::MODEL_PRODUCT:
return url('/product/index', ['categoryId' => $cate['id']]);
break;
default:
return url('/article/index', ['categoryId' => $cate['id']]);
break;
}
}
return $url;
}
}
//根据栏目获取文章路径
if (!function_exists('archiveGetUri')) {
function archiveGetUri($archive)
{
if (!empty($archive) &&isset($archive["id"])&& isset($archive->archivesCategory)&&!empty($archive->archivesCategory)) {
if (empty($archive->archivesCategory->route)) {
switch ($archive->archivesCategory["model_id"]){
case ModelModel::MODEL_PRODUCT:
return '/product/detail/'.$archive['id'].".html";
break;
default:
return '/article/detail/'.$archive['id'].".html";
break;
}
} else {
return $archive->archivesCategory->route."/".$archive['id'] .".html";
}
}
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('getTextareaText')){
function getTextareaText($content) {
$content = trim($content);
if(!empty($content)) {
$content = str_replace("\r\n", "\n", $content);
$content = str_replace("\r", "\n", $content);
}
return $content;
}
}
if(!function_exists('getTextNlToList')){
function getTextNlToList($content) {
$list = [];
$content = getTextareaText($content);
if(!empty($content)) {
$list = explode("\n", $content);
}
return $list;
}
}
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;
}
}
}
if (!function_exists('dateToFormat')) {
/**
* 时间日期格式化
*
* @param null|string $dateStr
* @param string $format
* @return string
*/
function dateToFormat(?string $dateStr, string $format='Y-m-d'): string
{
if (empty($dateStr)) {
return '';
}
$time = strtotime($dateStr);
if (!$time) {
return '';
}
return date($format, $time);
}
}
if (!function_exists('timeToFormat')) {
/**
* 时间日期格式化
*
* @param int $time
* @param string $format
* @return string
*/
function timeToFormat(int $time, string $format='Y-m-d'): string
{
if ($time <= 0) {
return '';
}
return date($format, $time);
}
}

62
app/controller/ApiManager.php Executable file
View File

@ -0,0 +1,62 @@
<?php
namespace app\controller;
use app\model\AuthRule;
use app\model\LoginLog;
use app\model\Member;
use app\service\Jwt;
class ApiManager extends Base
{
public function index()
{
// $jwtData = [
// 'member_id' => "11",
// ];
// $token = Jwt::generate($jwtData);
$token = input("token/s","");
$userInfo = Jwt::parse($token);//token中携带的简易用户信息
if(empty($userInfo)||!array_key_exists("member_id",$userInfo)){
return $this->json(4001,"无效请求");
}
$member = Member::getById($userInfo["member_id"]);
if(empty($member)){
return $this->json(4001,"用户不存在");
}
//执行登录
$rulesList = AuthRule::userRolesList($member['group_id']);
$rulesIdStr = '';
if (!empty($rulesList)) {
$rulesId = $rulesList['allRulesId'];
$rulesIdStr = implode(',', $rulesId);
}
$authSession = [
'userId' => $member['id'],
'userName' => $member['username'],
'groupId' => $member['group_id'],
'rules' => $rulesIdStr,
'cates' => $member['cates']
];
//记录最后登陆时间
$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', $authSession);
return $this->redirect("/manager");
}
}

158
app/controller/Article.php Executable file
View File

@ -0,0 +1,158 @@
<?php
namespace app\controller;
use app\model\{Article as MArticle,
Category,
Model
};
use page\DxtcPageA;
use think\Collection;
use think\Paginator;
class Article extends Base
{
//详情
public function detail($id = 0)
{
if ($id <= 0) {
return $this->error('错误页面');
}
$article = MArticle::getById($id);
if (empty($article)) {
return $this->error('无此文章');
}
MArticle::updateById($id, ['views' => $article['views'] + 1]);
$category = Category::getById($article['category_id']);
$description = $article['seo_description'] ?: $this->system['seo_description'];
$keywords = $article['seo_keywords'] ?: $this->system['seo_keywords'];
$title = $article['seo_title'] ?: $article['title'].' | '.$this->system['seo_title'];
$pathArr = explode(',', $category['path']);
$secondCategoryId = isset($pathArr[3]) && !empty($pathArr[3]) ? $pathArr[3] : $article['category_id'];
$secondCategory = Category::getById($secondCategoryId);
$this->data['images'] = json_decode($article['imgs'], true);
$this->setSeo($title, $keywords, $description);
$this->data['item'] = MArticle::parseInfo($article);
$this->data['category'] = $category;
$this->data['secondInfo'] = $secondCategory;
$this->data['categoryId'] = $category['id'];
$this->data['topCategoryId'] = Category::firstGradeById($category['id']) ;
$this->detailPrevAndNext($article);
$this->detailRecommendList($article, $category['recommend_num']);
return $this->view($category['template_detail'] ?? 'news_detail');
}
// 上下篇
private function detailPrevAndNext($article)
{
$prev = [];
$next = [];
if ($article) {
$sortCategoryIds = [];
/*
$caseCategoryIds = Category::getCategoryWithChildrenIds(Category::CATEGORY_COMMUNITY);
if(in_array($article['category_id'], $caseCategoryIds)) {
$sortCategoryIds = $caseCategoryIds;
}
*/
if (!empty($sortCategoryIds)) {
$prevWhere = [
['sort', '>', $article['sort']],
['category_id', 'in', $sortCategoryIds]
];
$prevOrder = ['a.sort' => 'asc'];
$prev = MArticle::getPrevArticleByTimeAndCategoryIds($prevWhere, $prevOrder);
$nextWhere = [
['sort', '<', $article['sort']],
['category_id', 'in', $sortCategoryIds]
];
$nextOrder = ['sort' => 'desc'];
$next = MArticle::getNextArticleByTimeAndCategoryIds($nextWhere, $nextOrder);
} else {
$prev = MArticle::getPrevArticleBySortAndCategoryId($article['sort'], $article['category_id']);
$next = MArticle::getNextArticleBySortAndCategoryId($article['sort'], $article['category_id']);
}
}
$this->data['prev'] = $prev;
$this->data['next'] = $next;
}
// 相关推荐
private function detailRecommendList($article, $num = 8)
{
// 目前规则为除当前以外的同栏目下最新8个
$orderList = ['top' => 'desc', 'recommend' => 'desc', 'id' => 'desc'];
$list = MArticle::getLastTopList($article['category_id'], $num, [$article['id']], $orderList);
$this->data['recommendList'] = $list;
}
//列表页
public function index($categoryId = 0)
{
$categoryId = empty($categoryId) ? $this->request->param("category_id") : $categoryId;
$category = Category::getById($categoryId);
if (!$category || $category['model_id'] != Model::MODEL_ARTICLE) {
return $this->error('错误页面');
}
$description = $category['seo_description'] ?: $this->system['seo_description'];
$keywords = $category['seo_keywords'] ?: $this->system['seo_keywords'];
$title = $category['seo_title'] ?: $category['title'].' | '.$this->system['seo_title'];
$this->setSeo($title, $keywords, $description);
// 自定义分页驱动
app('think\App')->bind(Paginator::class, DxtcPageA::class);
$this->data['items'] = MArticle::getList($categoryId,$category['number']??10);
$this->data['category'] = $category;
$this->data['categoryId'] = $categoryId;
$this->data['topCategoryId'] = Category::firstGradeById($category['id']) ;
$this->data['bodyClass'] = 'main';
return $this->view($category['template_list'] ?? '/news_list');
}
//文章列表接口,获取栏目下文章列表
public function ajaxList()
{
$categoryId = input('category_id/d', 0);
$keyword = input('keyword/s', '');
$page = input('page/s', 1);
if ($categoryId <= 0) {
return $this->json(1, '参数错误');
}
$category = Category::getById($categoryId);
if (empty($category)) {
return $this->json(2, '栏目不存在');
}
$items = MArticle::findListByWhere(["category_id"=>$categoryId],$page, $category['number']);
foreach ($items as $item) {
$item['uri'] = archiveGetUri($item);
$item['create_date_d'] = date('d', $item['create_time']);
$item['create_date_y_m'] = date('Y-m', $item['create_time']);
}
return $this->json(0, 'ok', $items);
}
}

122
app/controller/Base.php Executable file
View File

@ -0,0 +1,122 @@
<?php
namespace app\controller;
use app\model\Category;
use app\model\ConfigSetting;
use app\model\Link;
use app\model\System;
use app\model\VisitLogoModel;
use think\response\Redirect;
/**
* 控制器基础类
*/
class Base extends BaseController
{
//需要向模板传递的值
protected $data = [];
//系统配置信息
protected $system = [];
// 初始化
protected function initialize()
{
$this->system = System::getSystem();
$this->data['system'] = $this->system;
$this->data['links'] = Link::getList();
if (session('?__token__')) {
$this->data['_token'] = session('__token__');
} else {
$this->data['_token'] = $this->request->buildToken();
}
$this->data['config'] = ConfigSetting::getConfigContentsByName('extraBase');
$this->nav();
$this->logVisit();
$this->setNofollowATagNames();
}
//设置SEO信息
protected function setSeo($title, $keywords = '', $description = '')
{
$this->data['seoTitle'] = $title;
$this->data['seoKeywords'] = $keywords;
$this->data['seoDescription'] = $description;
}
//模板
protected function view($template = '')
{
return view($template)->assign($this->data);
}
public function setExtraBaseConfig()
{
$this->data['extraBase'] = ConfigSetting::getConfigContentsByName('extraBase');
}
/*
* 重构异常提示页面
*/
protected function error($msg = '', string $url = null, $data = [], int $code = 404): Redirect
{
switch ($code) {
case 500:
return $this->redirect('/500.html');
default:
return $this->redirect('/404.html');
}
}
// 记录访问日志
protected function logVisit()
{
$referer = $_SERVER["HTTP_REFERER"] ?? '';
$domain = $this->request->domain();
$checkDomain = substr($referer, 0, strlen($domain));
if (!empty($referer) && $domain !== $checkDomain) {
// session('first_landing_page', $this->request->url(true));
VisitLogoModel::create([
'ip' => request()->ip(),
'referer' => $referer,
'visit' => $this->request->url(true),
'create_time' => time(),
]);
}
}
// HTML a标签设置 rel="nofollow" 属性,禁止搜索引擎追踪
protected function setNofollowATagNames()
{
$this->data['nofollowList'] = [
];
$this->data['footerAllowFollowList'] = [
'客户案例', '公司新闻',
];
}
protected function nav()
{
$this->data['menus'] = Category::navList()['menus'];
}
// 所有下级栏目ID
private function getAllSubordinateIds($items, $curId = 0)
{
$list = [];
foreach ($items as $k => $item) {
if ($item['parent_id'] == $curId) {
$list[] = $item['id'];
unset($items[$k]);
$childrenIds = $this->getAllSubordinateIds($items, $item['id']);
if ($childrenIds) {
$list = array_merge($list, $childrenIds);
}
}
}
return $list;
}
}

241
app/controller/BaseController.php Executable file
View File

@ -0,0 +1,241 @@
<?php
declare (strict_types=1);
namespace app\controller;
use think\{App, Request, response\Json, response\Redirect, Validate};
use Exception;
use think\exception\ValidateException;
/**
* 控制器基础类
*/
abstract class BaseController
{
/**
* 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((string) url('error/jump', $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/jump', $result));
}
/**
* 返回封装后的API数据到客户端
* 以json格式抛出异常
* @access protected
* @param mixed $code 返回的code
* @param mixed $msg 提示信息
* @param mixed $data 要返回的数据
* @return Json
*/
protected function json($code = 0, $msg = 'ok', $data = []): Json
{
$result = [
'code' => $code,
'msg' => $msg,
'time' => time(),
'data' => $data
];
return json($result);
}
/**
* URL重定向
* @access protected
* @param mixed $url 跳转的URL表达式
* @return Redirect
*/
protected function redirect($url): Redirect
{
if (!is_string($url)) {
$url = $url->__toString();
}
return redirect($url);
}
}

56
app/controller/Download.php Executable file
View File

@ -0,0 +1,56 @@
<?php
namespace app\controller;
use app\model\{DownloadModel, Category};
use page\DxtcPageA;
use think\Paginator;
class Download extends Base
{
//列表页
public function index($categoryId = 0)
{
$rule = Category::RULE_DOWNLOAD;
$categoryId = empty($categoryId) ? $this->request->param("category_id") : $categoryId;
$category = Category::getById($categoryId);
//没有category_id 则通过路由查询
$category = $category ?: Category::getByRuleAlias($rule);
$description = $category['seo_description'] ?: $category['title'];
$keywords = $category['seo_keywords'] ?: $category['title'];
$title = $category['seo_title'] ?: $category['title'].' | '.$this->system['seo_title'];
$this->setSeo($title, $keywords, $description);
$listSort = ['a.sort' => 'desc'];
// 自定义分页驱动
app('think\App')->bind(Paginator::class, DxtcPageA::class);
$items = DownloadModel::getList($category['id'], $category['number'], '', [], 1, $listSort, false);
$items->each(function ($item) {
$item->size_text = sizeToStr($item->size);
});
$this->data['items'] = $items;
$this->data['category'] = $category;
$this->data['categoryId'] = $category['id'];
$this->data['bodyClass'] = 'main';
return $this->view();
}
public function file()
{
$id = input('id');
if (!$file = DownloadModel::getById($id)) {
return $this->error('文件不存在');
}
if (!file_exists(public_path().$file['file'])) {
return $this->error('文件不存在');
}
return download(public_path().$file['file'], $file['title'].'.'.$file['suffix']);
}
}

64
app/controller/Error.php Executable file
View File

@ -0,0 +1,64 @@
<?php
namespace app\controller;
use app\model\Category;
use app\model\Model;
use app\model\SpecialRoute;
class Error extends BaseController
{
protected $data = [];
public function __call($method, $args)
{
//特殊路径处理
$specialRoute = SpecialRoute::findOneByUrl($this->request->baseUrl());
if(!empty($specialRoute)){
switch ($specialRoute["type"]){
case SpecialRoute::type_archives:
return action("article/detail",["id"=>$specialRoute->relation_id]);
break;
case SpecialRoute::type_archives_category:
$category = Category::findInfoById($specialRoute->relation_id);
if(empty($category)){
break;
}
switch ($category->model_id){
case Model::MODEL_PRODUCT:
return action("product/index",["id"=>$specialRoute->relation_id]);
break;
case Model::MODEL_ARTICLE:
return action("article/index",["categoryId"=>$specialRoute->relation_id]);
break;
case Model::MODEL_PAGE:
return action("page/index",["categoryId"=>$specialRoute->relation_id]);
break;
}
break;
}
}
$this->data['seoTitle'] = '404';
return $this->redirect('/404.html');
}
public function jump()
{
$param = request()->param();
return view()->assign($param);
}
// 404
public function notFind()
{
$this->data['seoTitle'] = '404';
return view('error/404')->assign($this->data);
}
// 500
public function serviceFail()
{
$this->data['seoTitle'] = '500';
return view('error/500')->assign($this->data);
}
}

76
app/controller/Index.php Executable file
View File

@ -0,0 +1,76 @@
<?php
namespace app\controller;
use app\model\{Article as MArticle, Category, Block, Message, Article, Slide};
use Exception;
use think\exception\ValidateException;
use think\facade\View;
use app\service\Tool;
class Index extends Base
{
public function index()
{
$category = Category::getIndex();
$categoryId = $category['id'] ?? 0;
$blocks = Block::getByCategoryId($categoryId);
$this->news(5);
$this->data['categoryId'] = $categoryId;
$this->data['blocks'] = Block::convertValue($blocks);
$this->data['category'] = $category;
$this->data['topCategoryId'] = Category::firstGradeById($category['id']) ;
$this->data['isIndex'] = true;
$this->data['slide'] = Slide::getList();
$this->setSeo($this->system['seo_title'], $this->system['seo_keywords'], $this->system['seo_description']);
return $this->view();
}
// 新闻动态
private function news($num)
{
$this->data['newsList'] = MArticle::getIndexList(Category::CATEGORY_NEWS, $num);
$topNews = MArticle::getIndexTop(Category::CATEGORY_NEWS);
$this->data['topNews'] = $topNews;
}
/**
* 留言
*
* @throws Exception
*/
public function message()
{
if ($this->request->isPost()) {
$item = input('item/a', [], 'strip_tags');
$validate = $this->validateByApi($item, [
'code|验证码' => 'require',
'name|姓名' => 'require',
'email|邮箱' => 'email',
'tel|联系方式' => 'require|mobile',
'content|留言内容' => 'require',
]);
if ($validate !== true) {
return $validate;
}
if (!captcha_check($item['code'])) {
return $this->json(4001, '验证码错误');
}
Message::create([
'name' => $item['name'],
'tel' => $item['tel'],
'email' => $item['email'],
'content' => $item['content'],
'ip' => request()->ip(),
'create_time' => time(),
]);
return $this->json();
} else {
return $this->json('请求错误');
}
}
}

28
app/controller/Links.php Executable file
View File

@ -0,0 +1,28 @@
<?php
namespace app\controller;
use app\model\Link;
use page\DxtcPageA;
use think\Paginator;
/**
* 友情链接
*
* Class Links
* @package app\controller
*/
class Links extends Base
{
public function index()
{
// 自定义分页驱动
app('think\App')->bind(Paginator::class, DxtcPageA::class);
$items = Link::getListWithPaginate([], 15);
$this->data['items'] = $items;
$this->setSeo('友情链接');
return $this->view();
}
}

8
app/controller/Msg.php Executable file
View File

@ -0,0 +1,8 @@
<?php
namespace app\controller;
class Msg extends Base
{
}

54
app/controller/Page.php Executable file
View File

@ -0,0 +1,54 @@
<?php
namespace app\controller;
use app\model\{Category, Block, History, Honour, Model as MModel, Article as MArticle, PositionModel};
use think\response\View;
class Page extends Base
{
/**
* @return View|void
*/
public function index($categoryId=0)
{
$category = Category::getById($categoryId);
if (!empty($category)) {
$description = $category['seo_description'] ?: $this->system['seo_description'];
$keywords = $category['seo_keywords'] ?: $this->system['seo_keywords'];
$title = $category['seo_title'] ?: $category['title'].' | '.$this->system['seo_title'];
$this->setSeo($title, $keywords, $description);
} else {
$this->redirect('/404.html');
}
$childCategory = Category::getChildrenByParentId($category['id']);
$parentCategory = Category::getById($category['parent_id']);
$brotherCategory = Category::getChildrenByParentId($category['parent_id']);
$blocks = Block::getByCategoryId($category['id']);
$blocks = Block::convertValue($blocks);
// 父级单页配置的公共信息
$parentBlocks = [];
if (!empty($parentCategory) && $parentCategory['model_id'] == MModel::MODEL_PAGE) {
$parentBlocks = Block::getByCategoryId($parentCategory['id']);
$parentBlocks = Block::convertValue($parentBlocks);
}
$this->data['topCategoryId'] = Category::firstGradeById($category['id']) ;
$this->data['categoryId'] = $category['id'];
$this->data['category'] = $category;
$this->data['childCategory'] = $childCategory;
$this->data['blocks'] = $blocks;
$this->data['parentBlocks'] = $parentBlocks;
$this->data['parentCategory'] = $parentCategory;
$this->data['brotherCategory'] = $brotherCategory;
$this->data['bodyClass'] = 'main';
$this->setExtraBaseConfig();
return $this->view($category['template_detail']);
}
}

44
app/controller/Position.php Executable file
View File

@ -0,0 +1,44 @@
<?php
namespace app\controller;
use app\model\{Block, Category, PositionModel};
use think\Paginator;
class Position extends Base
{
//详情
public function detail($id = 0)
{
if ($id <= 0) {
return $this->error('错误页面');
}
$article = PositionModel::getById($id);
if (empty($article)) {
return $this->error('无此内容');
}
$category = Category::getById($article['category_id']);
$description = $category['seo_description'] ?: $this->system['seo_description'];
$keywords = $category['seo_keywords'] ?: $this->system['seo_keywords'];
$title = $category['seo_title'] ?: $category['title'].' | '.$this->system['seo_title'];
$this->setSeo($title, $keywords, $description);
$this->data['item'] = $article;
$this->data['category'] = $category;
$blocks = Block::getByCategoryId($category['id']);
$blocks = Block::convertValue($blocks);
$this->data['blocks'] = $blocks;
$this->data['categoryId'] = $category['id'];
$this->data['topCategoryId'] = Category::firstGradeById($category['id']) ;
return $this->view('/article/joindata');
}
}

131
app/controller/Product.php Executable file
View File

@ -0,0 +1,131 @@
<?php
namespace app\controller;
use app\model\{ProductModel,
Category,
Model
};
use page\DxtcPageA;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\Paginator;
class Product extends Base
{
//详情
public function detail($id = 0)
{
if ($id <= 0) {
return $this->error('错误页面');
}
$article = ProductModel::getById($id);
if (empty($article)) {
return $this->error('无此产品');
}
$category = Category::getById($article['category_id']);
$description = $article['seo_description'] ?: $this->system['seo_description'];
$keywords = $article['seo_keywords'] ?: $this->system['seo_keywords'];
$title = $article['seo_title'] ?: $article['title'].' | '.$this->system['seo_title'];
$pathArr = explode(',', $category['path']);
$secondCategoryId = $pathArr[3] ?: $article['category_id'];
$secondCategory = Category::getById($secondCategoryId);
$this->data['images'] = json_decode($article['images'], true);
// 相关推荐
$recommendList = $this->recommendList($article, 3);
$this->data['recommendList'] = $recommendList;
$this->setSeo($title, $keywords, $description);
$this->data['item'] = $article;
$this->data['category'] = $category;
$this->data['secondInfo'] = $secondCategory;
$this->data['categoryId'] = $category['id'];
$this->data['topCategoryId'] = Category::firstGradeById($category['id']) ;
return $this->view($category['template_detail'] ?? '');
}
/**
* 相关推荐
*
* @param $article
* @param $num
* @return array
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
private function recommendList($article, $num)
{
return ProductModel::where('category_id', $article['category_id'])
->where('visible', 1)
->order('sort', 'desc')
->order('id', 'desc')
->limit($num)
->select()->toArray();
}
//列表页
public function index()
{
$second = input('second/d', 0);
$third = input('third/d', 0);
$first = Category::getByRuleAlias(Category::RULE_PRODUCT);//产品顶级分类
$categoryList = Category::getChildrenByParentId($first['id'], false);
$secondMenus = [];
$secondChildren = [];
foreach ($categoryList as $k => $cate) {
if ($cate['parent_id'] == $first['id']) {
$secondMenus[$cate['id']] = $cate;
unset($categoryList[$k]);
}
}
foreach ($categoryList as $thirdCate) {
foreach ($secondMenus as $secondCate) {
if (!isset($secondChildren[$secondCate['id']])) {
$secondChildren[$secondCate['id']] = [];
}
if ($thirdCate['parent_id'] == $secondCate['id']) {
$secondChildren[$secondCate['id']][] = $thirdCate;
}
}
}
$secondInfo = $second == 0 ? array_values($secondMenus)[0] : $secondMenus[$second];
$thirdMenus = $secondChildren[$secondInfo['id']];
$this->data['thirdId'] = $third;
$description = $first['seo_description'] ?: $this->system['seo_description'];
$keywords = $first['seo_keywords'] ?: $this->system['seo_keywords'];
$title = $first['seo_title'] ?: $first['title'].' | '.$this->system['seo_title'];
$this->setSeo($title, $keywords, $description);
$listSort = ['a.sort' => 'desc'];
$this->data['secondInfo'] = $secondInfo;
$this->data['secondMenus'] = $secondMenus;
$this->data['thirdMenus'] = $thirdMenus;
// 自定义分页驱动
app('think\App')->bind(Paginator::class, DxtcPageA::class);
$cateId = $third > 0 ? $third : $secondInfo['id'];
$queryParam = ['second' => $secondInfo['id'], 'third' => $third];
$items = ProductModel::getList($cateId, $first['number'], '', [], 1, $listSort, false, $queryParam);
$this->data['items'] = $items;
$this->data['category'] = $first;
$this->data['categoryId'] = $first['id'];
$this->data['bodyClass'] = 'main';
$this->data['topCategoryId'] = Category::firstGradeById($first['id']) ;
return $this->view();
}
}

71
app/controller/Search.php Executable file
View File

@ -0,0 +1,71 @@
<?php
namespace app\controller;
use app\model\{Category, ProductModel};
use page\DxtcPageA;
use think\Paginator;
class Search extends Base
{
//列表页
public function index()
{
$type = input('type/s', 'product');
if (!in_array($type, ['product', 'news', 'scheme'])) {
return [];
}
$keyword = input('keyword/s', '');
if (empty($keyword)) {
return $this->error('请输入搜索关键词');
}
// 自定义分页驱动
app('think\App')->bind(Paginator::class, DxtcPageA::class);
$items = [];
$paginate = [
'list_rows' => 20,
'query' => ['keyword' => $keyword, 'type' => $type]
];
$orderList = ['sort' => 'desc', 'id' => 'desc'];
$path = '';
switch ($type) {
case 'product':
$items = ProductModel::where('title|description|content|params', 'like', '%'.$keyword.'%')
->where('visible', 1)
->order($orderList)
->paginate($paginate);
$path = '/product/detail.html';
break;
case 'news':
$categoryIds = Category::where('template_list', Category::TEMPLATE_NEWS)->column('id');
$items = \app\model\Article::where('title|summary|content', 'like', '%'.$keyword.'%')
->whereIn('category_id', $categoryIds)
->where('status', 1)
->order($orderList)
->paginate($paginate);
$path = '/news/detail.html';
break;
case 'scheme':
$categoryIds = Category::where('template_list', Category::TEMPLATE_SCHEME)->column('id');
$items = \app\model\Article::where('title|summary|content', 'like', '%'.$keyword.'%')
->whereIn('category_id', $categoryIds)
->where('status', 1)
->order($orderList)
->paginate($paginate);
$path = '/scheme/detail.html';
break;
}
$this->data['items'] = $items;
$this->data['bodyClass'] = 'main';
$this->data['type'] = $type;
$this->data['keyword'] = $keyword;
$this->data['path'] = $path;
return $this->view();
}
}

9
app/controller/Upload.php Executable file
View File

@ -0,0 +1,9 @@
<?php
namespace app\controller;
use app\controller\manager\Upload as MUpload;
class Upload extends MUpload
{
}

View File

@ -0,0 +1,506 @@
<?php
namespace app\controller\manager;
use app\model\{Article as MArticle, ArticleTags, Category, SpecialRoute, System, Log};
use app\validate\Article as VArticle;
use think\exception\ValidateException;
use app\service\Tool;
use think\facade\Db;
/**
* 内容管理 - 文章管理
*/
class Article extends Base
{
// 允许设置组图的模版列表
protected $allowImgTemplate = [
Category::TEMPLATE_CASES,
];
//批量修改属性
public function attribute()
{
if ($this->request->isPost()) {
$ids = input('post.id/a');
if (empty($ids) || !is_array($ids)) {
return $this->json(2, '参数错误,请核对之后再操作!');
}
$data = [];
foreach (['top', 'hot', 'recommend'] as $key) {
$val = input('post.'.$key, 0);
if (in_array($val, [1, 2])) {
if ($val == 1) {
$data[$key] = 1;
} else {
$data[$key] = 0;
}
}
}
if (!empty($data)) {
MArticle::whereIn('id', $ids)->update($data);
Log::write('article', 'attribute', '批量修改了文章属性涉及到的文章ID为'.implode(',', $ids));
}
return $this->json();
}
return $this->json(1, '非法请求!');
}
//批量删除
public function batchDel()
{
if ($this->request->isPost()) {
$ids = input('post.ids/a');
if (empty($ids) || !is_array($ids)) {
return $this->json(2, '参数错误,请核对之后再操作!');
}
$items = MArticle::getListByIds($ids);
if (empty($items)) {
return $this->json(3, '待删除文章列表为空');
}
Db::startTrans();
try {
$delIds = [];
$cateId = $items[0]['category_id'];
foreach ($items as $item) {
$delIds[] = $item['id'];
}
MArticle::destroy($delIds);
SpecialRoute::deleteByTypeIds($delIds ,SpecialRoute::type_archives);
Log::write('article', 'betchDel', '批量删除了文章涉及到的文章ID为'.implode(',', $delIds));
Db::commit();
} catch (\Exception $e) {
Db::rollback();
return $this->json(5000, '文章删除失败!');
}
return $this->json();
}
return $this->json(1, '非法请求!');
}
//删除
public function del()
{
if ($this->request->isPost()) {
$id = input('post.id/d');
if (is_numeric($id) && $id > 0) {
$item = MArticle::getById($id);
if (empty($item)) {
return $this->json(3, '待删除文章不存在');
}
Db::startTrans();
try {
MArticle::destroy($id);
Log::write('article', 'del', '删除文章ID'.$id.',标题:'.$item['title']);
SpecialRoute::deleteByTypeIds([$id] ,SpecialRoute::type_archives);
Db::commit();
} catch (\Exception $e) {
Db::rollback();
return $this->json(5000, '文章删除失败!');
}
return $this->json();
}
return $this->json(2, '参数错误,请核对之后再操作!');
}
return $this->json(1, '非法请求!');
}
//排序
public function sort()
{
if ($this->request->isPost()) {
$id = input('post.id/d');
$sort = input('post.sort');
$num = input('post.num/d', 1);
$categoryId = $this->request->param('category_id/d', 0); // get | post
if ($num <= 0) {
$num = 1;
}
if (!in_array($sort, ['up', 'down'], true)) {
return $this->json(2, '参数错误');
}
$item = MArticle::getById($id);
if (empty($item)) {
return $this->json(3, '该文章信息不存在');
}
$whereMap = [];
if (is_numeric($categoryId) && $categoryId > 0) {
$children = Category::getChildrenByParentId($categoryId);
if (!empty($children)) {
$childrenIds = [];
foreach ($children as $child) {
if ($child['model_id'] == 31) {
$childrenIds[] = $child['id'];
}
}
$whereMap[] = ['category_id', 'in', $childrenIds];
} else {
$whereMap[] = ['category_id', '=', $categoryId];
}
} else {
$whereMap[] = ['category_id', '=', $item['category_id']];
}
if ($sort == 'up') {
$whereMap[] = ['sort', '>', $item['sort']];
$order = "sort asc";
} else {
$whereMap[] = ['sort', '<', $item['sort']];
$order = "sort desc";
}
$forSortItems = MArticle::getListByWhereAndOrder($whereMap, $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 MArticle();
$model->saveAll($updateData);
$sortStr = $sort == 'up' ? '上移' : '下调';
Log::write('article', 'sort', "文章排序ID{$id} ,标题:{$item['title']}{$sortStr}{$num}");
return $this->json();
}
}
return $this->json(4, '无须调整排序!');
}
return $this->json(1, '非法请求!');
}
//编辑
public function edit()
{
if ($this->request->isPost()) {
$item = input('post.item/a');
$img = input('post.img', '');
$logo = input('post.img_logo', '');
$banner = input('post.img_banner', '');
$video = input('post.video', '');
$id = input('post.id/d');
$article = MArticle::getById($id);
if (empty($article)) {
return $this->json(1, '该文章不存在!');
}
$item['src'] = $img;
if (!empty($logo)) {
$item['logo'] = $logo;
}
if (!empty($banner)) {
$item['banner'] = $banner;
}
$item['video'] = $video;
try {
validate(VArticle::class)->scene("edit")->check($item);
$auth = session('auth');
$item['update_time'] = time();
$item['updated'] = $auth['userName'];
cache('articlesForRoute', null); //清理缓存
// 默认属性
$defaultAttributes = ['recommend', 'top', 'hot'];
$item['recommend'] = $item['recommend'] ?? 0;
$item['top'] = $item['top'] ?? 0;
$item['hot'] = $item['hot'] ?? 0;
// 自定义属性配置(包含默认属性)
$recommendOtherNameStr = input('post.recommend_other_str/s', '');
$recommendOtherList = [];
if (!empty($recommendOtherNameStr)) {
$recommendOtherNameList = explode(',', $recommendOtherNameStr);
$attributeList = MArticle::getAttributeList([$article['category_id']]);
$attributeKeyList = array_flip($attributeList);
foreach ($recommendOtherNameList as $recommendOtherName) {
$attributeKey = $attributeKeyList[$recommendOtherName] ?? '';
if (!empty($attributeKey)) {
$recommendOtherList[] = $attributeKey;
// 默认属性配置,适用于排序
if (in_array($attributeKey, $defaultAttributes)) {
$item[$attributeKey] = 1;
}
}
}
}
$item['recommend_other'] = empty($recommendOtherList) ? '' : implode(',', $recommendOtherList);
// 标签
$item['tag'] = trim($item['tag'] ?? '');
$item['tag'] = str_replace('', ',', $item['tag']);
if (!empty($tagSaveCheck) && ($article['status'] == MArticle::STATUS_NORMAL || $item['status'] == MArticle::STATUS_NORMAL)) {
$oldTagList = empty($article['tag']) ? [] : explode(',', $article['tag']);
$curTagList = empty($item['tag']) ? [] : explode(',', $item['tag']);
/**
* $oldTagList 差集total - 1
* $curTagList 差集不存在则新增存在则total + 1
* [normal->disable] total - 1
* [disable->normal] total + 1
*/
if ($article['status'] != $item['status']) {
if ($article['status'] == MArticle::STATUS_NORMAL) {
$curTagList = [];
} else {
$oldTagList = [];
}
}
$oldTagsDiff = array_diff($oldTagList, $curTagList);
$curTagsDiff = array_diff($curTagList, $oldTagList);
if (count($oldTagsDiff) > 0) {
ArticleTags::whereIn('name', $oldTagsDiff)
->where($tagSaveCheck, '>', 0)
->dec($tagSaveCheck, 1)
->update();
}
if (count($curTagsDiff) > 0) {
$hadTagItems = ArticleTags::findByNames($curTagsDiff);
$hadTags = $hadTagItems->column('name');
$newTags = array_diff($curTagsDiff, $hadTags);
$tagsInsert = [];
$tagsUpdate = [];
foreach ($newTags as $tagName) {
$tagsInsert[] = [
'name' => $tagName,
$tagSaveCheck => 1
];
}
foreach ($hadTagItems as $tagItem) {
$tagsUpdate[] = [
'id' => $tagItem['id'],
$tagSaveCheck => $tagItem[$tagSaveCheck] + 1
];
}
if (count($tagsInsert) > 0) {
ArticleTags::insertAll($tagsInsert);
}
if (count($tagsUpdate) > 0) {
(new ArticleTags())->saveAll($tagsUpdate);
}
}
}
MArticle::updateById($id, $item);
//处理特殊路由
if (array_key_exists("route", $item) && !empty($item['route'])) {
$specialRoute = SpecialRoute::findByTypeRelaTioneId($id,SpecialRoute::type_archives);
if(empty($specialRoute)){
$specialRouteData = [
"route" =>$item["route"]??'',
"type" =>SpecialRoute::type_archives,
"relation_id" =>$id,
];
SpecialRoute::create($specialRouteData);
}else{
$specialRoute->save(["route"=>$item["route"]??'']);
}
}else{
SpecialRoute::deleteByTypeIds([$id],SpecialRoute::type_archives);
}
Db::commit();
Log::write('article', 'edit', "文章编辑ID{$id} ,标题:{$item['title']}");
return $this->json();
}
catch (ValidateException $e) {
Db::rollback();
return $this->json(2, $e->getError());
}catch (\Exception $e) {
Db::rollback();
return $this->json(3, $e->getMessage());
}
} else {
$id = input('param.id');
$article = MArticle::getById($id);
$category = [];
$categoryId = $article['category_id'] ?? 0;
$imgSize = '';
$attributeList = [];
if ($article) {
$article = MArticle::convertRecommendOther([$categoryId], $article, false);
$attributeList = MArticle::getAttributeList([$categoryId]);
$category = Category::getById($categoryId);
if ($category['img_width'] && $category['img_height']) {
$imgSize = $category['img_width'].'像素 X '.$category['img_height'].'像素';
} else {
$imgSize = System::getArticleImageSize();
}
}
$this->data['item'] = $article;
$this->data['category'] = $category;
$this->data['imgSize'] = $imgSize;
$this->data['attributeList'] = $attributeList;
$this->data['contentRaw'] = in_array($categoryId, Category::$contentRawCategoryList);
$this->data['allowTag'] = in_array($categoryId, Category::$allowTagCategoryList);
$this->data['allowImgs'] = in_array($category['template_list'], $this->allowImgTemplate);
return $this->view();
}
}
//添加
public function add()
{
if ($this->request->isPost()) {
$item = input('post.item/a');
$img = input('post.img', '');
$logo = input('post.img_logo', '');
$banner = input('post.img_banner', '');
$video = input('post.video', '');
$item['src'] = $img;
if (!empty($logo)) {
$item['logo'] = $logo;
}
if (!empty($banner)) {
$item['banner'] = $banner;
}
$item['video'] = $video;
Db::startTrans();
try {
validate(VArticle::class)->scene("add")->check($item);
$content = $item['content'] ?? '';
if (isset($item['content'])) {
unset($item['content']);
}
$item['content'] = $content;
if (!empty($item['alias'])) {
if (Tool::hasAlias($item['alias'])) {
throw new ValidateException('别名已存在');
}
cache('articlesForRoute', null); //有别名时,清理缓存
}
// 默认属性
$defaultAttributes = ['recommend', 'top', 'hot'];
$item['recommend'] = $item['recommend'] ?? 0;
$item['top'] = $item['top'] ?? 0;
$item['hot'] = $item['hot'] ?? 0;
// 自定义属性配置
$recommendOtherNameStr = input('post.recommend_other_str', '');
$recommendOtherList = [];
if (!empty($recommendOtherNameStr)) {
$recommendOtherNameList = explode(',', $recommendOtherNameStr);
$attributeList = MArticle::getAttributeList([$item['category_id']]);
$attributeKeyList = array_flip($attributeList);
foreach ($recommendOtherNameList as $recommendOtherName) {
$attributeKey = $attributeKeyList[$recommendOtherName] ?? '';
if (!empty($attributeKey)) {
$recommendOtherList[] = $attributeKey;
// 默认属性配置,适用于排序
if (in_array($attributeKey, $defaultAttributes)) {
$item[$attributeKey] = 1;
}
}
}
}
$item['recommend_other'] = empty($recommendOtherList) ? '' : implode(',', $recommendOtherList);
// 标签
$item['tag'] = trim($item['tag'] ?? '');
$item['tag'] = str_replace('', ',', $item['tag']);
if (!empty($item['tag']) && !empty($tagSaveCheck) && $item['status'] == MArticle::STATUS_NORMAL) {
$curTags = explode(',', $item['tag']);
$tagItems = ArticleTags::findByNames($curTags);
$hadTags = $tagItems->column('name');
$newTags = array_diff($curTags, $hadTags);
if (count($newTags) > 0) {
$tagsInsert = [];
$tagsUpdate = [];
foreach ($newTags as $tag) {
$tagsInsert[] = [
'name' => $tag,
$tagSaveCheck => 1
];
}
foreach ($tagItems as $tagItem) {
$tagsUpdate[] = [
'id' => $tagItem['id'],
$tagSaveCheck => $tagItem[$tagSaveCheck] + 1,
];
}
if (count($tagsInsert) > 0) {
ArticleTags::insertAll($tagsInsert);
}
if (count($tagsUpdate)) {
(new ArticleTags())->saveAll($tagsUpdate);
}
}
}
$article = MArticle::create($item);
//处理特殊路由
if (array_key_exists("route", $item) && !empty($item['route'])) {
$specialRouteData = [
"route" =>$item["route"]??'',
"type" =>SpecialRoute::type_archives,
"relation_id" =>$article->id,
];
SpecialRoute::create($specialRouteData);
}
Log::write('article', 'add', "文章新增ID{$article->id} ,标题:{$item['title']}");
Db::commit();
return $this->json();
} catch (ValidateException $e) {
Db::rollback();
return $this->json(2, $e->getError());
} catch (\Exception $e) {
Db::rollback();
return $this->json(2, 'request fail! '.$e->getMessage());
}
} else {
$categoryId = input('param.category_id');
$category = Category::getById($categoryId);
if (count($category) > 0 && $category['img_width'] && $category['img_height']) {
$imgSize = $category['img_width'].'像素 X '.$category['img_height'].'像素';
} else {
$imgSize = System::getArticleImageSize();
}
$this->data['category'] = $category;
$this->data['imgSize'] = $imgSize;
$this->data['attributeList'] = MArticle::getAttributeList([$categoryId]);
$this->data['contentRaw'] = in_array($categoryId, Category::$contentRawCategoryList);
$this->data['allowTag'] = in_array($categoryId, Category::$allowTagCategoryList);
$this->data['allowImgs'] = in_array($category['template_list'], $this->allowImgTemplate);
return $this->view();
}
}
}

153
app/controller/manager/Backup.php Executable file
View File

@ -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, '非法请求');
}
}
}

89
app/controller/manager/Base.php Executable file
View File

@ -0,0 +1,89 @@
<?php
namespace app\controller\manager;
use app\controller\BaseController;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\Border;
use think\response\Redirect;
/**
* 控制器基础类
*/
class Base extends BaseController
{
// excel 导出格式配置
protected $excelStyle = [
'font' => [
'name' => '宋体',
],
'alignment' => [
'horizontal' => Alignment::HORIZONTAL_CENTER, // 水平居中
'vertical' => Alignment::VERTICAL_CENTER, // 垂直居中
'wrapText' => true,
],
'borders' => [
'allBorders' => [
'borderStyle' => Border::BORDER_THIN,
'color' => ['rgb' => 'eeeeee'],
]
],
];
protected $defaultSetting = [
'cell_width' => 30,
'font_size' => 12
];
protected $data = [];
protected function initialize()
{
$this->middleware = [
//'csrf',
'auth' => [
'except' => ['manager.login/index']
]
];
$auth = session('auth');
$this->data['member'] = $auth;
if (session('?__token__')) {
$this->data['_token'] = session('__token__');
} else {
$this->data['_token'] = $this->request->buildToken();
}
$this->data['groupId'] = $auth['groupId'] ?? 0;
}
//变量赋值到模板
protected function view()
{
return view()->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));
}
}

View File

@ -0,0 +1,327 @@
<?php
namespace app\controller\manager;
use app\model\{Category as MCategory, Model as MCModel, Log, SpecialRoute};
use app\validate\Category as VCategory;
use think\facade\Db;
use think\Exception;
use think\exception\ValidateException;
use think\facade\Cache;
use app\service\Tool;
class Category extends Base
{
//栏目列表
public function index()
{
$auth = session('auth');
$authCates = array_filter(explode(',', $auth['cates']));
if ($auth['groupId'] == 1) {
$cates = MCategory::getList();
} else {
$cates = MCategory::getList(true, $authCates);
}
$items = MCategory::getCates($cates);
$powerAdd = false;
$ruleNames = Cache::get('rule_names_'.$auth['groupId']);
if (is_array($ruleNames) && in_array('category/add', $ruleNames, true)) {
$powerAdd = true;
}
$this->data['items'] = $items;
$this->data['power_add'] = $powerAdd;
return $this->view();
}
//批量删除
public function batchDel()
{
if($this->request->isPost()){
$ids = input('post.ids/a');
if(is_array($ids)) {
$idsArr = $ids;
} else {
$idsArr = array_filter(explode(',', $ids));
}
if(count($idsArr) == 0) {
return $this->json(1, '无效请求,参数错误!');
}
if (MCategory::hasChildren($idsArr)) {
return $this->json(2, '需删除的栏目下存在下级栏目,不可删除。请检查核实后再操作!');
}
$categories = MCategory::getListByIds($idsArr);
if(!empty($categories)){
$hasIds = [];
foreach($categories as $cate){
$hasIds[] = $cate['id'];
}
MCategory::destroy($hasIds);
Cache::delete("categoryNames");
SpecialRoute::deleteByTypeIds($hasIds ,SpecialRoute::type_archives_category);
Log::write('category', 'betchDel', '批量删除了栏目涉及到的ID为' . implode(',', $hasIds));
return $this->json();
}
return $this->json(3, '删除失败!栏目不存在,请刷新页面后再试!');
}
return $this->json(1, '非法请求!');
}
/**
* 删除
*/
public function del()
{
if($this->request->isPost()){
$id = input('post.id/d');
if(is_numeric($id) && $id > 0) {
if(MCategory::hasChildren($id)){
return $this->json(4, '此栏目有下级栏目,不可删除。请检查核实后再操作!');
}
$cate = MCategory::getById($id);
if(!empty($cate)){
MCategory::destroy($id);
Cache::delete("categoryNames");
SpecialRoute::deleteByTypeIds([$id] ,SpecialRoute::type_archives_category);
Log::write('category', 'del', '删除栏目ID' . $id . ',标题:' . $cate['title']);
return $this->json();
}
return $this->json(3, '删除失败!栏目不存在,请刷新页面后再试!');
} else {
return $this->json(2, '无效请求,参数错误!');
}
}
return $this->json(1, '非法请求!');
}
//排序
public function sort()
{
if($this->request->isPost()){
$id = input('post.id/d');
$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 = MCategory::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 = MCategory::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 MCategory();
$model->saveAll($updateData);
$sortStr = $sort == 'up' ? '上移' : '下调';
Log::write('category', 'sort', "栏目排序ID{$id} ,标题:{$item['title']}{$sortStr}{$num}");
return $this->json();
}
}
return $this->json(4, '无须调整排序!');
}
return $this->json(1, '非法请求!');
}
/**
* 更新栏目数据
*/
public function edit()
{
if($this->request->isPost()){
$item = input('post.item/a');
$id = input('post.id');
$img = input('post.img');
$img_mobile = input('post.img_mobile');
$icon = input('post.imgicon');
if (count($item) > 0 && (is_numeric($id) === true && $id > 0)) {
Db::startTrans();
try {
validate(VCategory::class)->scene("edit")->check($item);
$item['src'] = empty($img) ? '' : $img;
$item['src_mobile'] = empty($img_mobile) ? '' : $img_mobile;
$item['icon_img'] = empty($icon) ? '' : $icon;
// 只允许文章类栏目可以设置汇总查看
if ($item['model_id'] != MCModel::MODEL_ARTICLE) {
$item['parent_show'] = 0;
}
if (isset($item['category_key']) && strlen($item['category_key']) > 0) {
$hadCate = MCategory::findByCategoryKey($item['category_key']);
if (!empty($hadCate) && $hadCate['id'] != $id) {
throw new ValidateException('栏目标识已存在');
}
}
MCategory::updateById($id, $item);
//处理特殊路由
if (array_key_exists("route", $item) && !empty($item['route'])) {
$specialRoute = SpecialRoute::findByTypeRelaTioneId($id,SpecialRoute::type_archives_category);
if(empty($specialRoute)){
$specialRouteData = [
"route" =>$item["route"]??'',
"type" =>SpecialRoute::type_archives_category,
"relation_id" =>$id,
];
SpecialRoute::create($specialRouteData);
}else{
$specialRoute->save(["route"=>$item["route"]??'']);
}
}else{
SpecialRoute::deleteByTypeIds($id,SpecialRoute::type_archives_category);
}
Db::commit();
Cache::delete("categoryNames");
Log::write('category', 'edit', "栏目编辑ID{$id} ,标题:{$item['title']}");
return $this->json();
} catch (ValidateException $e) {
Db::rollback();
return $this->json(2, $e->getError());
}catch (\Exception $e) {
return $this->json(2, $e->getMessage());
}
}
return $this->json(1,'传入参数错误,请核对之后再操作!');
} else {
$id = input('param.id');
if (is_numeric($id) === true && $id > 0) {
$item = MCategory::getById($id);
if(empty($item)){
return $this->json(1,'参数错误,无此栏目!');
}
$this->data['item'] = $item;
$this->data['parent'] = MCategory::getById($item['parent_id']);
$this->data['models'] = MCModel::getList();
$this->data['iconImgSize'] = '64px * 64px';
return $this->view();
}
return $this->json(1,'参数错误,请核对之后再操作!');
}
}
/**
* 栏目添加
*/
public function add()
{
if($this->request->isPost()){
$item = input('post.item/a');
$img = input('post.img');
$img_mobile = input('post.img_mobile');
$icon = input('post.imgicon');
if (is_array($item) === true && count($item) > 0) {
if (!empty($img)) {
$item['src'] = $img;
}if (!empty($img_mobile)) {
$item['src_mobile'] = $img_mobile;
}
if(!empty($icon)){
$item['icon_img'] = $icon;
}
Db::starttrans();
try {
validate(VCategory::class)->check($item);
if(!isset($item['number']) || $item['number'] <= 0){
$item['number'] = 20;
}
if($item['parent_id'] == 0){
$item['path'] = ',0,';
}else{
$parent = MCategory::getById($item['parent_id']);
if(empty($parent)){
$item['path'] = ',0,';
}else{
$item['path'] = $parent['path'] . $parent['id'] . ',';
}
}
// 只允许文章类栏目可以设置汇总查看
if ($item['model_id'] != MCModel::MODEL_ARTICLE) {
$item['parent_show'] = 0;
}
if (isset($item['category_key']) && strlen($item['category_key']) > 0) {
$hadCate = MCategory::findByCategoryKey($item['category_key']);
if (!empty($hadCate)) {
throw new ValidateException('栏目标识已存在');
}
}
$category = MCategory::create($item);
// 更新session
$member = \app\model\Member::getById(session('auth')['userId']);
$newCates = $member['cates'].','.$category['id'];
(new \app\model\Member())->where('id', $member['id'])->save([
'cates' => $newCates
]);
$auth = session('auth');
$auth['cates'] = $newCates;
session('auth', $auth);
//处理特殊路由
if (array_key_exists("route", $item) && !empty($item['route'])) {
$specialRouteData = [
"route" =>$item["route"]??'',
"type" =>SpecialRoute::type_archives_category,
"relation_id" =>$category['id'],
];
SpecialRoute::create($specialRouteData);
}
Cache::delete("categoryNames");
Db::commit();
Log::write('category', 'add', "栏目新增ID{$category->id} ,标题:{$item['title']}");
return $this->json();
} catch (ValidateException $e) {
return $this->json(2,$e->getError());
} catch (\Exception $e) {
return $this->json(2,$e->getMessage());
}
}
return $this->json(1, '传入值错误,请核对之后再操作!');
} else {
$parentId = input('param.id')??input('param.parent_id');
if(empty($parentId)){
$parent = [];
}else{
$parent = MCategory::getById($parentId);
}
$this->data['parent'] = $parent;
$this->data['models'] = MCModel::getList();
$this->data['iconImgSize'] = '64px * 64px';
return $this->view();
}
}
}

View File

@ -0,0 +1,95 @@
<?php
namespace app\controller\manager;
use app\model\ConfigSetting;
use think\facade\Config as CConfig;
/**
* 基础配置
* Class Config
* @package app\controller\manager
*/
class Config extends Base
{
public function base()
{
$configName = 'extraBase';
if ($this->request->isPost()) {
$data = input("post.");
if(isset($data['_token'])) {
unset($data['_token']);
}
ConfigSetting::setConfigByName($configName, $data);
return $this->json(0);
} else {
// 兼容写法,把原配置文件中的配置信息写入到数据库
$conf = ConfigSetting::getConfigContentsByName($configName);
if (empty($conf)) {
CConfig::load('extra/base', $configName);
$conf = config($configName);
if (!empty($conf)) {
ConfigSetting::setConfigByName($configName, $conf);
}
}
$this->data['item'] = $conf;
return $this->view();
}
}
//法律制度
public function statute()
{
$configName = 'extraStatute';
if ($this->request->isPost()) {
$data = input("post.");
if(isset($data['_token'])) {
unset($data['_token']);
}
ConfigSetting::setConfigByName($configName, $data);
return $this->json();
} else {
$conf = ConfigSetting::getConfigContentsByName($configName);
if (empty($conf)) {
CConfig::load('extra/statute', $configName);
$conf = config($configName);
if (!empty($conf)) {
ConfigSetting::setConfigByName($configName, $conf);
}
}
$this->data['item'] = $conf;
return $this->view();
}
}
/**
* 侧边栏配置
* @date 2021-11
*/
public function slide()
{
$configName = 'extraSlide';
if ($this->request->isPost()) {
$data = input("post.");
if(isset($data['_token'])) {
unset($data['_token']);
}
ConfigSetting::setConfigByName($configName, $data);
return $this->json();
} else {
$conf = ConfigSetting::getConfigContentsByName($configName);
$this->data['item'] = $conf;
return $this->view();
}
}
}

View File

@ -0,0 +1,258 @@
<?php
namespace app\controller\manager;
use app\model\{Category, Article, Block, DownloadModel, Log, History, Honour, PositionModel, ProductModel};
class Content extends Base
{
//文章
public function article()
{
$categoryId = input('param.category_id');
$keyword = input('param.keyword');
$param = input('param.param/a', []);
$category = Category::getById($categoryId);
if (empty($category)) {
return $this->error('无此栏目');
}
$order = ['top' => 'desc', 'sort' => 'desc'];
$list = Article::getList($categoryId, 20, $keyword, $param, -1, $order);
$list = Article::convertRecommendOther([$categoryId], $list, true);
$this->data['list'] = $list;
$this->data['category'] = $category;
$this->data['keyword'] = $keyword;
$this->data['param'] = $param;
$this->data['attributeList'] = Article::getAttributeList([$categoryId]);
return $this->view();
}
//做页面跳转
public function index()
{
$items = Category::getList();
if (!empty($items)) {
$items = Category::getCates($items);
}
if (!empty($items)) {
$first = array_shift($items);
if (isset($first['children'])) {
$childrenFirst = array_shift($first['children']);
$url = url('manager.content/'.$childrenFirst['manager'], ['category_id' => $childrenFirst['id']]);
} else {
$url = url('manager.content/'.$first['manager'], ['category_id' => $first['id']]);
}
if (!empty($url)) {
return $this->redirect($url);
}
} else {
return $this->redirect(url('manager.category/add'));
}
}
//单页
public function page()
{
if ($this->request->isAjax()) {
$blocks = input('post.block/a'); //所有文本信息
$texts = input('post.text/a'); //所有富文本信息
$codes = input('post.code/a'); //所有代码信息
$categoryId = input('post.category_id/d');
$category = Category::getById($categoryId);
unset($_POST['block']);
unset($_POST['text']);
unset($_POST['file']);
unset($_POST['code']);
$imgs = []; //图片信息
$videos = []; //视频信息
$groups = []; //组图信息
$groupIds = input('post.groupIds/a', []);
$videoGroup = []; //视频组信息
$videoGroupIds = input('post.videoGroupIds/a', []);
foreach ($_POST as $key => $val) {
if (strpos($key, '_') !== false) {
$keys = explode('_', $key);
if ($keys[1] == 'img') { //图片
$imgs[$keys[2]] = $val;
} elseif ($keys[1] == 'video') { //视频
$videos[$keys[2]][$keys[0]] = $val;
} elseif ($keys[1] == 'videos') { //视频组
$videoGroup[$keys[2]] = $val;
} elseif ($keys[1] == 'group') { //组图
$groups[$keys[2]] = $val;
}
}
}
$data = [];
if (!empty($blocks)) {
foreach ($blocks as $key => $block) {
$data[] = [
'id' => $key,
'value' => $block
];
}
}
if (!empty($texts)) {
foreach ($texts as $key => $text) {
$data[] = [
'id' => $key,
'value' => $text
];
}
}
if (!empty($codes)) {
foreach ($codes as $key => $code) {
$data[] = [
'id' => $key,
'value' => $code
];
}
}
if (!empty($imgs)) {
foreach ($imgs as $key => $img) {
$data[] = [
'id' => $key,
'value' => $img
];
}
}
if (!empty($videos)) {
foreach ($videos as $key => $video) {
$data[] = [
'id' => $key,
'img' => $video['img'],
'value' => $video['video']
];
}
}
if (!empty($groupIds)) {
foreach ($groupIds as $key => $groupId) {
$group = $groups[$groupId] ?? [];
$data[] = [
'id' => $groupId,
'value' => json_encode($group)
];
}
}
if (!empty($videoGroupIds)) {
foreach ($videoGroupIds as $key => $groupId) {
$group = $videoGroup[$groupId] ?? [];
$data[] = [
'id' => $groupId,
'value' => json_encode($group)
];
}
}
$block = new Block;
$block->saveAll($data);
Log::write('content', 'page', "单页编辑栏目ID{$category['id']} ,标题:{$category['title']}");
return $this->json();
} else {
$categoryId = input('param.category_id');
$category = Category::getById($categoryId);
/*
* 单页栏目允许父级栏目配置,不自动跳转到第一个子栏目
$children = Category::getChildrenByParentId($categoryId);
if(!empty($children)){
$child = Category::getChildrenByParentId($children[0]['id']);
if(empty($child)){
$category = $children[0];
}else{
$category = $child[0];
}
}
*/
if (empty($category)) {
return $this->redirect(url('manager.content/index'));
}
$blocks = Block::getByCategoryId($category['id']);
$this->data['categoryId'] = $categoryId;
$this->data['category'] = $category;
$this->data['blocks'] = $blocks;
$this->data['groupId'] = session('auth.groupId');
$this->data['types'] = Block::getTypes();
return $this->view();
}
}
// 发展历程
public function history()
{
$categoryId = input('param.category_id/d', 0);
$category = Category::getById($categoryId);
if (empty($category)) {
return $this->redirect(url('manager.content/index'));
}
$this->data['categoryId'] = $categoryId;
$this->data['category'] = $category;
$this->data['items'] = History::getPaginateList($categoryId, 20, false);
return $this->view();
}
// 荣誉资质
public function honour()
{
$categoryId = input('param.category_id/d', 0);
$category = Category::getById($categoryId);
if (empty($category)) {
return $this->redirect(url('manager.content/index'));
}
$this->data['categoryId'] = $categoryId;
$this->data['category'] = $category;
$this->data['items'] = Honour::getPaginateList($categoryId, 20, false);
return $this->view();
}
// 产品
public function product()
{
$categoryId = input('param.category_id/d', 0);
$category = Category::getById($categoryId);
if (empty($category)) {
return $this->redirect(url('manager.content/index'));
}
$this->data['categoryId'] = $categoryId;
$this->data['category'] = $category;
$this->data['items'] = ProductModel::getPaginateList($categoryId, 20, false);
return $this->view();
}
// 下载中心
public function download()
{
$categoryId = input('param.category_id/d', 0);
$category = Category::getById($categoryId);
if (empty($category)) {
return $this->redirect(url('manager.content/index'));
}
$this->data['categoryId'] = $categoryId;
$this->data['category'] = $category;
$this->data['items'] = DownloadModel::getPaginateList($categoryId, 20, false);
return $this->view();
}
// 招聘职位
public function position()
{
$categoryId = input('param.category_id/d', 0);
$category = Category::getById($categoryId);
if (empty($category)) {
return $this->redirect(url('manager.content/index'));
}
$this->data['categoryId'] = $categoryId;
$this->data['category'] = $category;
$this->data['items'] = PositionModel::getPaginateList($categoryId, 20, false);
return $this->view();
}
}

View File

@ -0,0 +1,164 @@
<?php
namespace app\controller\manager;
use app\model\{DownloadModel, Category as MCategory, Log as MLog, System};
use think\exception\ValidateException;
use app\validate\{DownloadValidate};
use think\facade\Db;
/**
* 下载中心
* Class Download
* @package app\controller\manager
*/
class Download extends Base
{
public function add()
{
if (request()->isPost()) {
$params = input('post.item/a', []);
$file = input('file');
$params = arrayHtmlFilter($params);
try {
$fileInfo = \app\model\File::where('src', $file)->order('id', 'desc')->find();
if (!$fileInfo) {
return $this->json(1, '文件不存在,请重新上传');
}
$params['size'] = $fileInfo['size'];
$params['mime_type'] = $fileInfo['mime_type'];
$params['suffix'] = $fileInfo['suffix'];
$params['file'] = $fileInfo['src'];
validate(DownloadValidate::class)->check($params);
$newItem = DownloadModel::create($params);
MLog::write('download', 'add', '新增下载中心ID:'.$newItem->id);
} catch (ValidateException $e) {
return $this->json(1, $e->getError());
}
return $this->json();
} else {
$categoryId = input('param.category_id/d', 0);
$category = MCategory::getById($categoryId);
$this->data['category'] = $category;
return $this->view();
}
}
public function edit()
{
$id = input('param.id/d', 0);
$item = DownloadModel::getById($id);
if (count($item) == 0) {
return $this->json(1, '该记录不存在');
}
if (request()->isPost()) {
$params = input('post.item/a', []);
$file = input('file');
$params = arrayHtmlFilter($params);
try {
if (!empty($file)) {
$fileInfo = \app\model\File::where('src', $file)->order('id', 'desc')->find();
if (!$fileInfo) {
return $this->json(1, '文件不存在,请重新上传');
}
$params['size'] = $fileInfo['size'];
$params['mime_type'] = $fileInfo['mime_type'];
$params['suffix'] = $fileInfo['suffix'];
$params['file'] = $fileInfo['src'];
}
validate(DownloadValidate::class)->check($params);
DownloadModel::updateById($id, $params);
MLog::write('download', 'edit', '修改下载中心ID:'.$id);
} catch (ValidateException $e) {
return $this->json(2, $e->getError());
}
return $this->json();
} else {
$this->data['item'] = $item;
return $this->view();
}
}
public function sort()
{
if (request()->isPost()) {
$id = input('post.id/d');
$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 = DownloadModel::getById($id);
if (empty($item)) {
return $this->json(3, '该记录不存在');
}
if ($sort == 'up') {
$where = "category_id='{$item['category_id']}' and sort < {$item['sort']}";
$order = "sort desc";
} else {
$where = "category_id='{$item['category_id']}' and sort > {$item['sort']}";
$order = "sort asc";
}
$forSortItems = DownloadModel::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 DownloadModel();
$model->saveAll($updateData);
$sortStr = $sort == 'up' ? '上移' : '下调';
MLog::write('download', 'sort', "下载中心排序ID{$id} {$sortStr}{$num}");
return $this->json();
}
}
return $this->json(4, '无须调整排序!');
}
return $this->json(1, '无此操作');
}
// 删除
public function del()
{
if (request()->isPost()) {
$downloadId = input('param.id/d', 0);
$item = DownloadModel::getById($downloadId);
if (count($item) == 0) {
return $this->json(2, '该记录不存在');
}
Db::startTrans();
try {
@unlink(public_path().$item['file'] ?? '');
DownloadModel::destroy($downloadId);
MLog::write('download', 'del', '删除历程ID:'.$downloadId);
Db::commit();
} catch (\Exception $e) {
Db::rollback();
return $this->json(3, '删除失败,'.$e->getMessage());
}
return $this->json();
}
return $this->json(1, '无此操作');
}
}

View File

@ -0,0 +1,33 @@
<?php
namespace app\controller\manager;
class Error
{
protected $data = [];
public function __call($method, $args)
{
$this->data['seoTitle'] = '404';
return view('error/404')->assign($this->data);
}
public function jump()
{
$param = request()->param();
return view()->assign($param);
}
// 404
public function notFind()
{
$this->data['seoTitle'] = '404';
return view('error/404')->assign($this->data);
}
// 500
public function serviceFail()
{
$this->data['seoTitle'] = '500';
return view('error/500')->assign($this->data);
}
}

146
app/controller/manager/File.php Executable file
View File

@ -0,0 +1,146 @@
<?php
namespace app\controller\manager;
use app\model\{File as MFile, Article, Block, Category, Link, LinkProduct, Slide, Log};
use app\service\Tool;
class File extends Base
{
//删除磁盘上的文件
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);
}
$linkProductFiles = LinkProduct::getFilesInUse();
if(!empty($linkProductFiles)){
$files = array_merge($files, $linkProductFiles);
}
$categoryFiles = Category::getFilesInUse();
if(!empty($categoryFiles)){
$files = array_merge($files, $categoryFiles);
}
$articleFiles = Article::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();
}
}

146
app/controller/manager/Group.php Executable file
View File

@ -0,0 +1,146 @@
<?php
namespace app\controller\manager;
use app\model\{AuthGroup, AuthRule, Log};
use app\validate\AuthGroup as VAuthGroup;
use think\exception\ValidateException;
/**
* 角色管理控制器
*/
class Group extends Base
{
/**
* 角色、分组删除
*/
public function del()
{
if ($this->request->isPost()) {
$id = input('post.id/d');
if (is_numeric($id) === true && $id > 0) {
$item = AuthGroup::getById($id);
if(!empty($item)){
AuthGroup::destroy($id);
Log::write('group', 'del', '删除角色ID' . $id . ',名称:' . $item['title']);
return $this->json();
}
}
return $this->json(2, '传入参数错误,请核对之后再操作!');
}
return $this->json(1, '非法请求!');
}
/**
* 角色、分组权限分配
*/
public function rule()
{
if($this->request->isPost()){
$rules = input('post.rules/a');
$groupId = input('post.group_id/d');
if (is_array($rules) && (is_numeric($groupId) === true && $groupId > 0)) {
$group = AuthGroup::getById($groupId);
if(empty($group)){
return $this->json(2, '无此角色信息,请核对之后再操作!');
}
AuthGroup::updateRules($groupId, $rules);
// 重置该角色对应的权限缓存
AuthGroup::resetGroupRulesCache($groupId);
Log::write('group', 'rule', '角色分配权限ID' . $groupId . ',名称:' . $group['title']);
return $this->json();
}else{
return $this->json(3, '传入参数错误,请核对之后再操作!');
}
} else {
$groupId = input('param.group_id/d');
$group = AuthGroup::getById($groupId);
if(!empty($group)){
$rules = AuthRule::getListTree();
$this->data['group_id'] = $groupId;
$this->data['group'] = $group;
$this->data['rules'] = $rules;
return $this->view();
}else{
return $this->json(1, '无此角色信息,请核对之后再操作!');
}
}
}
/**
* 角色、分组添加
* @param int $status 1:正常0禁止
*/
public function add()
{
if($this->request->isPost()){
$title = trim(input('post.title'));
$status = input('post.status/d');
if (!empty($title) && (is_numeric($status) === true) && ($status == 1 || $status == 0)) {
$item = [
'title' => $title,
'status' => $status
];
try {
validate(VAuthGroup::class)->check($item);
$group = AuthGroup::create($item);
Log::write('group', 'add', "角色新增ID{$group->id} ,标题:{$group->title}");
return $this->json();
} catch (ValidateException $e) {
return $this->json(2, $e->getError());
}
}
return $this->json(1, '传入参数错误,请核对之后再操作!');
}else{
return $this->view();
}
}
/**
* 角色、分组编辑
*/
public function edit()
{
if($this->request->isPost()){
$title = trim(input('post.title'));
$status = input('post.status/d');
$id = input('post.id/d');
if (!empty($title) && ($status == 1 || $status == 0) && (is_numeric($id) === true && $id > 0)) {
$item = [
'title' => $title,
'status' => $status
];
try {
validate(VAuthGroup::class)->check($item);
AuthGroup::updateById($id, $item);
Log::write('group', 'edit', "角色编辑ID{$id} ,标题:{$item['title']}");
return $this->json();
} catch (ValidateException $e) {
return $this->json(2, $e->getError());
}
}
return $this->json(1, '传入参数错误,请核对之后再操作!');
}else{
$id = input('param.id/d');
if (is_numeric($id) === true && $id > 0) {
$item = AuthGroup::getById($id);
$this->data['item'] = $item;
return $this->view();
}
return $this->json(1, '传入参数错误,请核对之后再操作!');
}
}
/**
* 所有角色分组信息
* @return void
*/
public function index()
{
$auth = session('auth');
if ($auth['groupId'] == 1) {
$list = AuthGroup::select()->toArray();
} else {
$list = AuthGroup::where('id', '<>', 1)->select()->toArray();
}
$this->data['list'] = $list;
return $this->view();
}
}

View File

@ -0,0 +1,346 @@
<?php
namespace app\controller\manager;
use app\model\{HistoryInfo as MHistoryInfo, History as MHistory, Category as MCategory, Log as MLog, System};
use think\exception\ValidateException;
use app\validate\{History as VHistory, HistoryInfo as VHistoryInfo};
use think\facade\Db;
/**
* 发展历程
* Class History
* @package app\controller\manager
*/
class History extends Base
{
public function add()
{
if (request()->isPost()) {
$params = input('post.item/a', []);
$params = arrayHtmlFilter($params);
try {
validate(VHistory::class)->check($params);
$data = [
'title' => $params['title'],
'visible' => $params['visible'],
'category_id' => $params['category_id'],
'event' => $params['event'],
];
$newItem = MHistory::create($data);
MLog::write('history', 'add', '新增发展历程ID:'.$newItem->id);
} catch (ValidateException $e) {
return $this->json(1, $e->getError());
}
return $this->json();
} else {
$categoryId = input('param.category_id/d', 0);
$category = MCategory::getById($categoryId);
$this->data['category'] = $category;
return $this->view();
}
}
public function edit()
{
$id = input('param.id/d', 0);
$item = MHistory::getById($id);
if (count($item) == 0) {
return $this->json(1, '该历程信息不存在');
}
if (request()->isPost()) {
$params = input('post.item/a', []);
$params = arrayHtmlFilter($params);
try {
validate(VHistory::class)->check($params);
$data = [
'title' => $params['title'],
'visible' => $params['visible'],
'event' => $params['event'],
];
MHistory::updateById($id, $data);
MLog::write('history', 'edit', '修改发展历程ID:'.$id);
} catch (ValidateException $e) {
return $this->json(2, $e->getError());
}
return $this->json();
} else {
$this->data['item'] = $item;
return $this->view();
}
}
public function sort()
{
if (request()->isPost()) {
$id = input('post.id/d');
$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 = MHistory::getById($id);
if (empty($item)) {
return $this->json(3, '该历程信息不存在');
}
if ($sort == 'up') {
$where = "category_id='{$item['category_id']}' and sort < {$item['sort']}";
$order = "sort desc";
} else {
$where = "category_id='{$item['category_id']}' and sort > {$item['sort']}";
$order = "sort asc";
}
$forSortItems = MHistory::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 MHistory();
$model->saveAll($updateData);
$sortStr = $sort == 'up' ? '上移' : '下调';
MLog::write('history', 'sort', "发展历程排序ID{$id} {$sortStr}{$num}");
return $this->json();
}
}
return $this->json(4, '无须调整排序!');
}
return $this->json(1, '无此操作');
}
// 删除历程和历程相关的事例
public function del()
{
if (request()->isPost()) {
$historyId = input('param.id/d', 0);
$item = MHistory::getById($historyId);
if (count($item) == 0) {
return $this->json(2, '该历程信息不存在');
}
Db::startTrans();
try {
MHistory::destroy($historyId);
$hasInfo = MHistoryInfo::countByHistoryId($historyId);
if ($hasInfo > 0) {
MHistoryInfo::delByHistoryId($historyId);
}
MLog::write('history', 'del', '删除历程ID:'.$historyId);
Db::commit();
} catch (\Exception $e) {
Db::rollback();
return $this->json(3, '删除失败,'.$e->getMessage());
}
return $this->json();
}
return $this->json(1, '无此操作');
}
public function info()
{
$historyId = input('param.history_id/d', 0);
$history = MHistory::getById($historyId);
$infoItems = [];
$categoryId = $history['category_id'] ?? 0;
if (count($history) > 0) {
$infoItems = MHistoryInfo::getByHistoryId($historyId);
}
$this->data['history'] = $history;
$this->data['categoryId'] = $categoryId;
$this->data['items'] = $infoItems;
return $this->view();
}
// 新增发展历程详情
public function addInfo()
{
$historyId = input('param.history_id/d', 0);
$history = MHistory::getById($historyId);
if (count($history) == 0) {
return $this->json(1, '该历程信息不存在');
}
if (request()->isPost()) {
$params = input('post.item/a', []);
$params = arrayHtmlFilter($params);
$imgs = input('post.img/a');
if (!empty($imgs) && is_array($imgs)) {
$imgs = json_encode($imgs);
} else {
$imgs = '';
}
try {
validate(VHistoryInfo::class)->check($params);
$data = [
'title' => $params['title'],
'visible' => $params['visible'],
'history_id' => $historyId,
'imgs' => $imgs,
];
$newItem = MHistoryInfo::create($data);
MLog::write('history', 'addInfo', '新增发展历程事例ID:'.$newItem->id);
} catch (ValidateException $e) {
return $this->json(2, $e->getError());
}
return $this->json();
} else {
$imgSize = '';
$category = MCategory::getById($history['category_id']);
if (count($category) > 0 && $category['img_width'] && $category['img_height']) {
$imgSize = $category['img_width'].'像素 X '.$category['img_height'].'像素';
}
$this->data['historyId'] = $historyId;
$this->data['history'] = $history;
$this->data['imgSize'] = $imgSize;
return $this->view();
}
}
// 编辑发展历程详情
public function editInfo()
{
$id = input('param.id/d', 0);
$item = MHistoryInfo::getById($id);
if (count($item) == 0) {
return $this->json(1, '该历程事例信息不存在');
}
if (request()->isPost()) {
$params = input('post.item/a', []);
$params = arrayHtmlFilter($params);
$imgs = input('post.img/a');
if (!empty($imgs) && is_array($imgs)) {
$imgs = json_encode($imgs);
} else {
$imgs = '';
}
try {
validate(VHistoryInfo::class)->check($params);
$data = [
'title' => $params['title'],
'visible' => $params['visible'],
'imgs' => $imgs,
];
MHistoryInfo::updateById($id, $data);
MLog::write('history', 'editInfo', '修改发展历程事例ID:'.$id);
} catch (ValidateException $e) {
return $this->json(2, $e->getError());
}
return $this->json();
} else {
$history = MHistory::getById($item['history_id']);
$imgSize = '';
if (count($history) > 0) {
$category = MCategory::getById($history['category_id']);
if (count($category) > 0 && $category['img_width'] && $category['img_height']) {
$imgSize = $category['img_width'].'像素 X '.$category['img_height'].'像素';
}
}
$this->data['item'] = $item;
$this->data['imgSize'] = $imgSize;
return $this->view();
}
}
public function sortInfo()
{
if (request()->isPost()) {
$id = input('post.id/d');
$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 = MHistoryInfo::getById($id);
if (empty($item)) {
return $this->json(3, '该历程事例信息不存在');
}
if ($sort == 'up') {
$where = "history_id='{$item['history_id']}' and sort < {$item['sort']}";
$order = "sort desc";
} else {
$where = "history_id='{$item['history_id']}' and sort > {$item['sort']}";
$order = "sort asc";
}
$forSortItems = MHistoryInfo::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 MHistoryInfo();
$model->saveAll($updateData);
$sortStr = $sort == 'up' ? '上移' : '下调';
MLog::write('history', 'sortInfo', "发展历程事例排序ID{$id} {$sortStr}{$num}");
return $this->json();
}
}
return $this->json(4, '无须调整排序!');
}
return $this->json(1, '无此操作');
}
public function delInfo()
{
if (request()->isPost()) {
$infoIds = [];
$ids = input('post.ids', []);
$id = input('post.id', 0);
if (!empty($ids)) {
if (is_array($ids)) {
$infoIds = $ids;
} else {
$infoIds = explode(',', $ids);
}
} elseif ($id > 0) {
$infoIds[] = $id;
}
if (count($infoIds) > 0) {
MHistoryInfo::destroy($infoIds);
MLog::write('history', 'delInfo', '删除历程事例IDs:'.implode(',', $infoIds));
return $this->json();
}
return $this->json(2, '参数错误');
}
return $this->json(1, '无此操作');
}
}

164
app/controller/manager/Honour.php Executable file
View File

@ -0,0 +1,164 @@
<?php
namespace app\controller\manager;
use app\validate\HonourValidate;
use app\model\{Honour as MHonour, Category as MCategory, Log as MLog, System};
use think\exception\ValidateException;
use think\response\Json;
use think\response\View;
/**
* 荣誉资质
* Class Honour
* @package app\controller\manager
*/
class Honour extends Base
{
/**
* @return Json|View
*/
public function add()
{
if (request()->isPost()) {
$params = input('post.item/a', []);
$params = arrayHtmlFilter($params);
$params['image'] = input('img');
try {
validate(HonourValidate::class)->check($params);
$newItem = MHonour::create($params);
MLog::write('honour', 'add', '新增荣誉资质ID:'.$newItem->id);
} catch (ValidateException $e) {
return $this->json(1, $e->getError());
}
return $this->json();
} else {
$categoryId = input('param.category_id/d', 0);
$category = MCategory::getById($categoryId);
if (count($category) > 0 && $category['img_width'] && $category['img_height']) {
$imgSize = $category['img_width'].'像素 X '.$category['img_height'].'像素';
} else {
$imgSize = System::getArticleImageSize();
}
$this->data['imgSize'] = $imgSize;
$this->data['category'] = $category;
return $this->view();
}
}
/**
* @return Json|View
*/
public function edit()
{
$id = input('param.id/d', 0);
$item = MHonour::getById($id);
if (count($item) == 0) {
return $this->json(1, '该荣誉资质不存在');
}
if (request()->isPost()) {
$params = input('post.item/a', []);
$params = arrayHtmlFilter($params);
$params['image'] = input('img');
try {
validate(HonourValidate::class)->check($params);
MHonour::updateById($id, $params);
MLog::write('honour', 'edit', '修改荣誉资质ID:'.$id);
} catch (ValidateException $e) {
return $this->json(2, $e->getError());
}
return $this->json();
} else {
$category = MCategory::getById($item['category_id']);
if (count($category) > 0 && $category['img_width'] && $category['img_height']) {
$imgSize = $category['img_width'].'像素 X '.$category['img_height'].'像素';
} else {
$imgSize = System::getArticleImageSize();
}
$this->data['imgSize'] = $imgSize;
$this->data['item'] = $item;
return $this->view();
}
}
public function sort()
{
if (request()->isPost()) {
$id = input('post.id/d');
$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 = MHonour::getById($id);
if (empty($item)) {
return $this->json(3, '该荣誉资质不存在');
}
if ($sort == 'up') {
$where = "category_id='{$item['category_id']}' and sort < {$item['sort']}";
$order = "sort desc";
} else {
$where = "category_id='{$item['category_id']}' and sort > {$item['sort']}";
$order = "sort asc";
}
$forSortItems = MHonour::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 MHonour();
$model->saveAll($updateData);
$sortStr = $sort == 'up' ? '上移' : '下调';
MLog::write('history', 'sort', "荣誉资质排序ID{$id} {$sortStr}{$num}");
return $this->json();
}
}
return $this->json(4, '无须调整排序!');
}
return $this->json(1, '无此操作');
}
// 删除历程和历程相关的事例
public function del()
{
if (request()->isPost()) {
$historyId = input('param.id/d', 0);
$item = MHonour::getById($historyId);
if (count($item) == 0) {
return $this->json(2, '该荣誉资质不存在');
}
try {
MHonour::destroy($historyId);
MLog::write('honour', 'del', '删除荣誉资质ID:'.$historyId);
} catch (\Exception $e) {
return $this->json(3, '删除失败,'.$e->getMessage());
}
return $this->json();
}
return $this->json(1, '无此操作');
}
}

View File

@ -0,0 +1,42 @@
<?php
namespace app\controller\manager;
use app\model\VisitLogoModel;
use think\response\View;
class Index extends Base
{
//后台首页
public function index(): View
{
$today = [];
$today[] = ['create_time', '>', strtotime(date('Y-m-d 00:00:00'))];
$today[] = ['create_time', '<', strtotime(date('Y-m-d 23:59:59'))];
// 全部
$data['total'] = VisitLogoModel::field('id')->count();
// 今日全部
$data['todayTotal'] = VisitLogoModel::where($today)->field('id')->count();
// 百度全部
$data['totalBaidu'] = VisitLogoModel::where('referer', 'like', '%baidu.com%')->field('id')->count();
// 360全部
$data['total360'] = VisitLogoModel::where('referer', 'like', '%so.com%')->field('id')->count();
// 百度今日
$data['todayBaidu'] = VisitLogoModel::where($today)->where('referer', 'like', '%baidu.com%')->field('id')->count();
// 360今日
$data['today360'] = VisitLogoModel::where($today)->where('referer', 'like', '%so.com%')->field('id')->count();
// 今日留言
$data['todayMessage'] = \app\model\Message::where($today)->count();
// 总留言
$data['totalMessage'] = \app\model\Message::count();
$this->data['data'] = $data;
return $this->view();
}
}

177
app/controller/manager/Link.php Executable file
View File

@ -0,0 +1,177 @@
<?php
namespace app\controller\manager;
use app\model\{Link as MLink, System, Log};
use app\validate\Link as VLink;
use think\exception\ValidateException;
class Link extends Base
{
//批量删除
public function batchDel()
{
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', 'betchDel', '批量删除了友情链接涉及到的ID为' . implode(',', $delIds));
return $this->json();
}else{
return $this->json(3, '待删除友情链接为空');
}
}
return $this->json(1, '非法请求!');
}
//删除
public function del()
{
if ($this->request->isPost()) {
$id = input('post.id/d');
if(is_numeric($id) && $id > 0) {
$item = MLink::getById($id);
if(!empty($item)){
MLink::destroy($id);
Log::write('link', 'del', '删除友情链接ID' . $id . ',标题:' . $item['title']);
return $this->json();
}
return $this->json(3, '待删除友情链接不存在');
}
return $this->json(2, '参数错误,请核对之后再操作!');
}
return $this->json(1, '非法请求!');
}
//排序
public function sort()
{
if($this->request->isPost()){
$id = input('post.id/d');
$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 = MLink::getById($id);
if(empty($item)){
return $this->json(3, '该友情链接信息不存在!');
}
if($sort == 'up'){
$where = "sort < {$item['sort']}";
$order = "sort desc";
}else{
$where = "sort > {$item['sort']}";
$order = "sort asc";
}
$forSortItems = MLink::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 MLink();
$model->saveAll($updateData);
$sortStr = $sort == 'up' ? '上移' : '下调';
Log::write('link', 'sort', "友情链接排序ID{$id} ,标题:{$item['title']}{$sortStr}{$num}");
return $this->json();
}
}
return $this->json(4, '无须调整排序!');
}
return $this->json(1, '非法请求!');
}
//编辑
public function edit()
{
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::getById($id);
if(empty($link)) {
return $this->json(2, '该友情链接信息不存在!');
}
if(!empty($img)){
$item['src'] = $img;
}
try {
validate(VLink::class)->check($item);
MLink::updateById($id, $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()
{
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()
{
$items = MLink::getListWithPaginate([], 50);
$this->data['items'] = $items;
return $this->view();
}
}

View File

@ -0,0 +1,179 @@
<?php
namespace app\controller\manager;
use app\model\{LinkProduct as MLinkProduct, System, Log};
use app\validate\LinkProduct as VLinkProduct;
use think\exception\ValidateException;
class LinkProduct extends Base
{
// 封面图推荐尺寸
protected $imgSize = '210像素 x 190像素';
//批量删除
public function batchDel()
{
if ($this->request->isPost()) {
$ids = input('post.ids/a');
if(empty($ids) || !is_array($ids)) {
return $this->json(2, '参数错误,请核对之后再操作!');
}
$items = MLinkProduct::getListByIds($ids);
if(!empty($items)){
$delIds = [];
foreach($items as $item){
$delIds[] = $item['id'];
}
MLinkProduct::destroy($delIds);
Log::write('LinkProduct', 'betchDel', '批量删除了合作伙伴涉及到的ID为' . implode(',', $delIds));
return $this->json();
}else{
return $this->json(3, '待删除合作伙伴为空');
}
}
return $this->json(1, '非法请求!');
}
//删除
public function del()
{
if ($this->request->isPost()) {
$id = input('post.id/d');
if(is_numeric($id) && $id > 0) {
$item = MLinkProduct::getById($id);
if(!empty($item)){
MLinkProduct::destroy($id);
Log::write('LinkProduct', 'del', '删除合作伙伴ID' . $id . ',标题:' . $item['title']);
return $this->json();
}
return $this->json(3, '待删除合作伙伴不存在');
}
return $this->json(2, '参数错误,请核对之后再操作!');
}
return $this->json(1, '非法请求!');
}
//排序 (数字小的在前)
public function sort()
{
if($this->request->isPost()){
$id = input('post.id/d');
$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 = MLinkProduct::getById($id);
if(empty($item)){
return $this->json(3, '该合作伙伴信息不存在!');
}
if($sort == 'up'){
$where = "sort < {$item['sort']}";
$order = "sort desc";
}else{
$where = "sort > {$item['sort']}";
$order = "sort asc";
}
$forSortItems = MLinkProduct::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 MLinkProduct();
$model->saveAll($updateData);
$sortStr = $sort == 'up' ? '上移' : '下调';
Log::write('LinkProduct', 'sort', "合作伙伴排序ID{$id} ,标题:{$item['title']}{$sortStr}{$num}");
return $this->json();
}
}
return $this->json(4, '无须调整排序!');
}
return $this->json(1, '非法请求!');
}
//编辑
public function edit()
{
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 = MLinkProduct::getById($id);
if(empty($link)) {
return $this->json(2, '该合作伙伴信息不存在!');
}
if(!empty($img)){
$item['src'] = $img;
}
try {
validate(VLinkProduct::class)->check($item);
MLinkProduct::updateById($id, $item);
Log::write('LinkProduct', '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 = MLinkProduct::getById($id);
$this->data['item'] = $item;
$this->data['img_size'] = $this->imgSize;
return $this->view();
}
}
//添加
public function add()
{
if($this->request->isPost()){
$item = input('post.item/a');
$img = input('post.img');
if(!empty($img)){
$item['src'] = $img;
}
try {
validate(VLinkProduct::class)->check($item);
$link = MLinkProduct::create($item);
Log::write('LinkProduct', 'add', "合作伙伴新增ID{$link->id} ,标题:{$item['title']}");
return $this->json();
} catch (ValidateException $e) {
return $this->json(2, $e->getError());
}
} else {
$this->data['img_size'] = $this->imgSize;
return $this->view();
}
}
public function index()
{
$items = MLinkProduct::getList();
$this->data['items'] = $items;
return $this->view();
}
}

View File

@ -0,0 +1,82 @@
<?php
namespace app\controller\manager;
use app\model\{Member, AuthRule, LoginLog};
use app\controller\BaseController;
class Login extends BaseController
{
/**
* user lgoin
* use ajax post push
*
* @return void | JSON
*/
public function index()
{
if(request()->isPost()){
$username = trim(input('param.username'));
$password = trim(input('param.password'));
$loginUrl = url('manager.login/index');
$captcha = trim(input('param.captcha', ''));
if (!captcha_check($captcha)) {
session('loginError','验证码错误');
return $this->redirect($loginUrl);
}
if(empty($username) || empty($password)){
session('loginError','用户名和密码不能为空');
return $this->redirect($loginUrl);
}
$member = Member::getByUserName($username);
if(empty($member)){
session('loginError','用户名错误');
return $this->redirect($loginUrl);
}
if($member['password'] != md5($password)){
session('loginError','用户密码错误');
return $this->redirect($loginUrl);
}
$rulesList = AuthRule::userRolesList($member['group_id']);
$rulesIdStr = '';
if (!empty($rulesList)) {
$rulesId = $rulesList['allRulesId'];
$rulesIdStr = implode(',', $rulesId);
}
$authSession = [
'userId' => $member['id'],
'userName' => $member['username'],
'groupId' => $member['group_id'],
'rules' => $rulesIdStr,
'cates' => $member['cates']
];
//记录最后登陆时间
$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', $authSession);
return redirect(url('manager.index/index'));
}
$viewData = [];
if(session('?loginError')) {
$viewData['error'] = session('loginError');
}
session('loginError', null);
return view()->assign($viewData);
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace app\controller\manager;
use app\controller\BaseController;
class Logout extends BaseController
{
public function index()
{
session(null);
return redirect(url('manager.login/index'));
}
}

181
app/controller/manager/Member.php Executable file
View File

@ -0,0 +1,181 @@
<?php
namespace app\controller\manager;
use app\model\{Category, AuthGroup, Member as MMember, Log};
use Exception;
use think\facade\Db;
class Member extends Base
{
/**
* 删除管理用户
*/
public function del()
{
if ($this->request->isPost()) {
$id = input('post.id/d');
if (is_numeric($id) === true && $id > 0) {
$item = MMember::getByID($id);
if(!empty($item)){
MMember::destroy($id);
Log::write('member', 'del', "管理员删除ID{$id}, 管理员:{$item['username']}");
return $this->json();
}
}
return $this->json(2, '参数错误,请核对之后再操作!');
}
return $this->json(1, '非法请求!');
}
/**
* 修改管理用户信息
* 由于try语法中抛出的异常类型与$this->json()抛出的异常类型不一致,因此需要利用$errorMsg 来判断返回情况
*/
public function edit()
{
if($this->request->isPost()){
$id = input('post.id/d');
$username = trim(input('post.username'));
$password = trim(input('post.password'));
$groupId = input('post.group_id/d');
if ((is_numeric($id) === true && $id > 0) && ((is_numeric($groupId) === true && $groupId > 0) && !empty($username))) {
$member = MMember::getByUserName($username);
if(!empty($member) && $member['id'] != $id){
return $this->json(2, '该用户名已被使用!');
}
$errorMsg = '';
Db::startTrans();
try {
$member = MMember::getById($id);
$item = [
'username' => $username,
'group_id' => $groupId
];
//角色权限重新赋值
$group = AuthGroup::getById($groupId);
$item['rules'] = $group['rules'];
if(!empty($password)){
$item['password'] = md5($password);
}
MMember::updateById($id, $item);
Log::write('member', 'edit', "管理员编辑ID{$id}, 管理员:{$item['username']}");
Db::commit();
} catch (Exception $e) {
Db::rollback();
$errorMsg = '用户信息修改失败!'.$e->getMessage();
}
if (empty($errorMsg)) {
return $this->json();
}
return $this->json(3, $errorMsg);
}
return $this->json(1, '参数错误,请核对之后再操作!');
}else{
$id = input('param.id/d');
if (is_numeric($id) === true && $id > 0) {
$member = MMember::getByID($id);
$item = [
'id' => $member['id'],
'username' => $member['username'],
'group_id' => $member['group_id']
];
$auth = session('auth');
$groups = AuthGroup::getListById($auth['groupId']);
$this->data['groups'] = $groups;
$this->data['item'] = $item;
return $this->view();
}
return $this->json(1, '参数错误,请核对之后再操作!');
}
}
/**
* 新增管理用户
*/
public function add()
{
if($this->request->isPost()){
$groupId = input('post.group_id/d');
$username = trim(input('post.username'));
$password = trim(input('post.password'));
if ((is_numeric($groupId) === true && $groupId > 0) && ($username != "" && $password != "")) {
$member = MMember::getByUserName($username);
if(!empty($member)){
return $this->json(2, '该用户名已被使用!');
}
$group = AuthGroup::getById($groupId);
$newMember = MMember::create([
'username' => $username,
'group_id' => $groupId,
'password' => md5($password),
'rules' => $group['rules'] ?? '',
'cates' => '',
'login_time' => 0,
]);
Log::write('member', 'add', "管理员新增ID{$newMember->id}, 管理员:{$newMember['username']}");
return $this->json();
}
return $this->json(1, '参数错误,请核对之后再操作!');
}
$auth = session('auth');
$groups = AuthGroup::getListById($auth['groupId']);
$this->data['groups'] = $groups;
return $this->view();
}
/**
* 栏目菜单分配
*/
public function menuAlloter()
{
if(request()->isPost()) {
$cates = input('post.cates/a');
$id = input('post.id/d');
if (is_array($cates) && (is_numeric($id) === true && $id > 0)) {
$member = MMember::getById($id);
if(empty($member)){
return $this->json(2, '无此用户信息,请核对之后再操作!');
}
MMember::updateCates($id, $cates);
Log::write('member', 'menuAlloter', "管理员栏目分配ID{$id}, 管理员:{$member['username']}");
return $this->json();
}else{
return $this->json(3, '传入参数错误,请核对之后再操作!');
}
} else {
$id = input('param.id/d');
if (is_numeric($id) && $id > 0) {
$member = MMember::getById($id);
if (empty($member)) {
return $this->json(2, '该管理员信息不存在,请核对之后再操作!');
}
$cates = Category::getListTree(false);
$memberCates = array_filter(explode(',', $member['cates']));
$this->data['id'] = $id;
$this->data['member'] = $member;
$this->data['memberCates'] = $memberCates;
$this->data['cates'] = $cates;
return $this->view();
}
return $this->json(1, '参数错误,请核对之后再操作!',$id);
}
}
/**
* 所有用户列表
*/
public function index()
{
$auth = session('auth');
if ($auth['groupId'] == 1) {
$items = MMember::getList(40);
} else {
$items = MMember::getListNotAdmin(40);
}
$this->data['items'] = $items;
return $this->view();
}
}

View File

@ -0,0 +1,141 @@
<?php
namespace app\controller\manager;
use app\model\{Message as MMessage, Log};
use app\service\Tool;
use app\service\File;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\Border;
class Message extends Base
{
protected $excelStyle = [
'font' => [
'name' => '宋体',
],
'alignment' => [
'horizontal' => Alignment::HORIZONTAL_CENTER, // 水平居中
'vertical' => Alignment::VERTICAL_CENTER, // 垂直居中
'wrapText' => true,
],
'borders' => [
'allBorders' => [
'borderStyle' => Border::BORDER_THIN,
'color' => ['rgb' => 'eeeeee'],
]
],
];
protected $defaultSetting = [
'cell_width' => 30,
'font_size' => 12
];
//下载文件
public function file()
{
$id = input('param.id/d');
if(is_numeric($id) && $id > 0) {
$item = MMessage::getById($id);
if(empty($item)){
return $this->error('留言不存在');
}
if(empty($item['file'])){
return $this->error('无文件需要下载');
}
$file = app()->getRootPath() . 'public' . $item['file'];
if(!file_exists($file)){
return $this->error('无文件需要下载');
}
$fileInfo = pathinfo($file);
$fileName = $item['name'] . '上传文件.' . $fileInfo['extension'];
$fileSize = filesize($file);
//以只读和二进制模式打开文件
$file = fopen ( $file, "rb" );
//告诉浏览器这是一个文件流格式的文件
Header ( "Content-type: application/octet-stream" );
//请求范围的度量单位
Header ( "Accept-Ranges: bytes" );
//Content-Length是指定包含于请求或响应中数据的字节长度
Header ( "Accept-Length: " . $fileSize );
//用来告诉浏览器,文件是可以当做附件被下载,下载后的文件名称为$file_name该变量的值。
Header ( "Content-Disposition: attachment; filename=" . $fileName );
//读取文件内容并直接输出到浏览器
echo fread ( $file, $fileSize );
fclose ( $file );
exit ();
}
return $this->error('参数错误,请核对之后再操作!');
}
//可以删除一个
public function del()
{
if ($this->request->isPost()) {
$id = input('post.id/d');
if(is_numeric($id) && $id > 0) {
$item = MMessage::getById($id);
if(!empty($item)){
MMessage::destroy($id);
if(!empty($item['file'])){
Tool::delFile($item['file']);
}
Log::write('link', 'del', '删除留言ID' . $id . ',姓名:' . $item['name']);
return $this->json();
}
return $this->json(3, '待删除留言不存在');
}
return $this->json(2, '参数错误,请核对之后再操作!');
}
return $this->json(1, '非法请求!');
}
//列表
public function index()
{
$startDate = input('param.startDate', '');
$endDate = input('param.endDate', '');
$items = MMessage::getList(20, $startDate, $endDate);
$this->data['items'] = $items;
$this->data['startDate'] = $startDate;
$this->data['endDate'] = $endDate;
return $this->view();
}
// 导出留言
public function export()
{
File::cancelTimeLimit();
$startDate = input('param.startDate', '');
$endDate = input('param.endDate', '');
$list = MMessage::getExportList($startDate, $endDate, 10000);
$spreadsheet = new Spreadsheet();
$header = ['序号', '姓名', '电话', '公司/团队名称', '申请日期'];
$sheet = $spreadsheet->getActiveSheet();
$sheetTitle = '预约记录';
$cellValues = [];
$cellWidthList = [];
foreach ($list as $item) {
$cellValues[] = [
[$item['id'], DataType::TYPE_STRING],
$item['name'],
$item['tel'],
$item['company'],
date('Y-m-d H:i', $item['create_time']),
];
}
File::setExcelCells($sheet, $cellValues, $header, $sheetTitle, $cellWidthList, $this->excelStyle, $this->defaultSetting);
File::export($spreadsheet, '预约记录导出_' . date('YmdHis') . '.xlsx');
}
}

184
app/controller/manager/Model.php Executable file
View File

@ -0,0 +1,184 @@
<?php
namespace app\controller\manager;
use app\model\{Model as MModel, Log};
use app\validate\Model as VModel;
use think\exception\ValidateException;
class Model extends Base
{
//批量删除模型
public function batchDel()
{
if($this->request->isPost()){
$ids = input('post.ids/a');
if(is_array($ids)) {
$idsArr = $ids;
} else {
$idsArr = array_filter(explode(',', $ids));
}
if(count($idsArr) == 0) {
return $this->json(1, '无效请求,参数错误!');
}
$items = MModel::getListByIds($idsArr);
if(!empty($items)){
$delIds = [];
foreach($items as $item){
$delIds[] = $item['id'];
}
MModel::destroy($delIds);
Log::write('model', 'batchDel', "模型批量删除ID" . implode(',', $ids));
return $this->json();
}
return $this->json(2, '无效请求,参数错误!');
}
return $this->json(1, '非法请求!');
}
//删除单个模型
public function del()
{
if($this->request->isPost()){
$id = input('post.id/d');
if(is_numeric($id) && $id > 0) {
$item = MModel::getById($id);
if(!empty($item)) {
MModel::destroy($id);
Log::write('model', 'del', "模型删除ID{$id}, 标题:{$item['name']}");
return $this->json();
}
return $this->json(3, '删除失败!该模型不存在,请刷新页面后再试!');
}
return $this->json(2, '无效请求,参数错误!');
}
return $this->json(1, '非法请求!');
}
//编辑模型
public function edit()
{
if($this->request->isPost()){
$item = [];
$item['name'] = input('post.name');
$item['template'] = input('post.template');
$item['manager'] = input('post.manager');
$id = input('post.id/d');
if(is_numeric($id) && $id > 0) {
$model = MModel::getById($id);
if(empty($model)){
return $this->json(2, '无此模型数据!');
}
try {
validate(VModel::class)->check($item);
MModel::updateById($id, $item);
Log::write('model', 'edit', "模型编辑ID{$id}, 标题:{$item['name']}");
return $this->json();
} catch (ValidateException $e) {
return $this->json(3, $e->getError());
}
}
return $this->json(1, '无效请求,参数错误!');
}else{
$id = input('param.id/d');
if (is_numeric($id) && $id > 0) {
$item = MModel::getById($id);
$this->data['item'] = $item;
return $this->view();
}
return $this->json(1,'传入参数错误,请核对之后再操作!');
}
}
/**
* 添加模型
*/
public function add()
{
if($this->request->isPost()){
$item = [];
$item['name'] = input('post.name');
$item['template'] = input('post.template');
$item['manager'] = input('post.manager');
try {
validate(VModel::class)->check($item);
$model = MModel::create($item);
Log::write('model', 'add', "模型新增ID{$model->id}, 标题:{$item['name']}");
return $this->json();
} catch (ValidateException $e) {
return $this->json(2, $e->getError());
}
}else {
return $this->view();
}
}
/**
* 模型列表
*/
public function index()
{
$items = MModel::getList();
$this->data['items'] = $items;
return $this->view();
}
/**
* 排序
*/
public function sort()
{
if($this->request->isPost()){
$id = input('post.id/d');
$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 = MModel::getById($id);
if(empty($item)) {
return $this->json(3, '无此模型!');
}
if($sort == 'up'){
$where = "sort < {$item['sort']}";
$order = "sort desc";
}else{
$where = "sort > {$item['sort']}";
$order = "sort asc";
}
$forSortItems = MModel::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 MModel();
$model->saveAll($updateData);
$sortStr = $sort == 'up' ? '上移' : '下调';
Log::write('model', 'sort', "模型排序ID{$id} ,标题:{$item['name']}{$sortStr}{$num}");
return $this->json();
}
}
return $this->json(4, '无须调整排序!');
}
return $this->json(1, '非法请求!');
}
}

672
app/controller/manager/Page.php Executable file
View File

@ -0,0 +1,672 @@
<?php
namespace app\controller\manager;
use app\model\{Category, Block, Log};
use app\validate\Block as VBlock;
use think\exception\ValidateException;
use app\service\Tool;
class Page extends Base
{
//源码,代码
public function code()
{
if ($this->request->isPost()) {
$item = input('post.item/a');
$categoryId = input('post.category_id/d');
$id = input('post.id/d');
$item['keyword'] = Tool::trimSpace($item['keyword']);
try {
validate(VBlock::class)->check($item);
if (empty($item['value'])) {
return $this->json(2, '内容不可为空!');
}
$block = Block::getByKeyword($item['keyword'], $categoryId);
if ($id) {
if (!empty($block) && $block['id'] != $id) {
return $this->json(3, '键值已存在,请更改键值');
}
Block::updateById($id, $item);
Log::write('page', 'code', "单页代码编辑ID{$id}, 键值:{$item['keyword']}");
} else {
if ($categoryId <= 0) {
return $this->json(4, '栏目参数错误!');
}
if (!empty($block)) {
return $this->json(3, '键值已存在,请更改键值');
}
$item['category_id'] = $categoryId;
$item['type'] = Block::CODE;
$block = Block::create($item);
Log::write('page', 'code', "单页代码新增ID{$block->id}, 键值:{$item['keyword']}");
}
return $this->json();
} catch (ValidateException $e) {
return $this->json(1, $e->getError());
}
} else {
$id = input('param.id/d');
if ($id <= 0) { //添加
$categoryId = input('param.category_id/d');
$category = Category::getById($categoryId);
if (empty($category)) {
$url = url('manager.content/index')->__toString();
return $this->error('无此栏目', $url);
}
} else { //修改
$item = Block::getById($id);
if (empty($item)) {
return $this->error('无此代码块!');
}
$categoryId = $item['category_id'];
$this->data['item'] = $item;
}
$this->data['categoryId'] = $categoryId;
return $this->view();
}
}
//排序
public function sort()
{
if ($this->request->isPost()) {
$id = input('post.id/d');
$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 = Block::getById($id);
if (empty($item)) {
return $this->json(3, '无此块信息');
}
if ($sort == 'up') {
$where = "category_id='{$item['category_id']}' and sort < {$item['sort']}";
$order = "sort desc";
} else {
$where = "category_id='{$item['category_id']}' and sort > {$item['sort']}";
$order = "sort asc";
}
$forSortItems = Block::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 Block();
$model->saveAll($updateData);
$sortStr = $sort == 'up' ? '上移' : '下调';
Log::write('page', 'sort', "单页区块排序ID{$id} ,键值:{$item['keyword']}{$sortStr}{$num}");
return $this->json();
}
}
return $this->json(4, '无须调整排序!');
}
return $this->error('无此操作');
}
//删除
public function del()
{
if ($this->request->isAjax()) {
$id = input('post.id/d');
$item = Block::getById($id);
if (!empty($item)) {
Block::destroy($id);
Log::write('page', 'del', "单页区块删除ID{$id} ,键值:{$item['keyword']}");
return $this->json();
}
return $this->json(1, 'fail');
}
return $this->error('无此操作');
}
//图片
public function img()
{
if ($this->request->isPost()) {
$item = input('post.item/a');
$img = trim(input('post.img'));
$categoryId = input('post.category_id/d');
if (!empty($img) && $img == 'null') {
$img = '';
}
$id = input('post.id/d');
$item['keyword'] = Tool::trimSpace($item['keyword']);
try {
validate(VBlock::class)->check($item);
$block = Block::getByKeyword($item['keyword'], $categoryId);
if ($id) {
if (!empty($block) && $block['id'] != $id) {
return $this->json(4, '键值已存在,请更改键值');
}
if (!empty($img)) {
$item['value'] = $img;
}
Block::updateById($id, $item);
Log::write('page', 'img', "单页图片编辑ID{$id} ,键值:{$item['keyword']}");
} else {
if (!empty($block)) {
return $this->json(4, '键值已存在,请更改键值');
}
if ($categoryId <= 0) {
return $this->json(2, '栏目参数错误!!');
}
if (empty($img)) {
return $this->json(3, '图片不可为空');
}
$item['value'] = $img;
$item['type'] = Block::IMG;
$item['category_id'] = $categoryId;
$block = Block::create($item);
Log::write('page', 'img', "单页图片新增ID{$block->id} ,键值:{$item['keyword']}");
}
return $this->json();
} catch (ValidateException $e) {
return $this->json(1, $e->getError());
}
} else {
$id = input('param.id/d');
if ($id <= 0) { //添加
$categoryId = input('param.category_id/d');
$category = Category::getById($categoryId);
if (empty($category)) {
$url = url('manager.content/index')->__toString();
return $this->error('无此栏目', $url);
}
} else { //修改
$item = Block::getById($id);
if (empty($item)) {
return $this->error('无此图片!');
}
$categoryId = $item['category_id'];
$this->data['item'] = $item;
}
if (isset($item) && $item['width'] && $item['height']) {
$imgSize = $item['width'] . 'px X ' . $item['height'] . 'px';
} else {
$imgSize = '';
}
$this->data['categoryId'] = $categoryId;
$this->data['imgSize'] = $imgSize;
$this->data['groupId'] = session('auth.groupId');
return $this->view();
}
}
//文字块
public function block()
{
if ($this->request->isPost()) {
$item = input('post.item/a');
$categoryId = input('post.category_id/d');
$id = input('post.id/d');
$item['keyword'] = Tool::trimSpace($item['keyword']);
try {
validate(VBlock::class)->check($item);
/* 允许空值
if(empty($item['value'])){
return $this->json(1, '内容不可为空!');
}
*/
$block = Block::getByKeyword($item['keyword'], $categoryId);
if ($id) {
if (!empty($block) && $block['id'] != $id) {
return $this->json(4, '键值已存在,请更改键值');
}
Block::updateById($id, $item);
Log::write('page', 'block', "单页文字块编辑ID{$id} ,键值:{$item['keyword']}");
} else {
if ($categoryId <= 0) {
return $this->json(2, '栏目参数错误!');
}
if (!empty($block)) {
return $this->json(4, '键值已存在,请更改键值');
}
$item['category_id'] = $categoryId;
$block = Block::create($item);
Log::write('page', 'block', "单页文字块新增ID{$block->id} ,键值:{$item['keyword']}");
}
return $this->json();
} catch (ValidateException $e) {
return $this->json(1, $e->getError());
}
} else {
$id = input('param.id/d');
if ($id <= 0) { //添加
$categoryId = input('param.category_id/d');
$category = Category::getById($categoryId);
if (empty($category)) {
$url = url('manager.content/index')->__toString();
return $this->error('无此栏目', $url);
}
} else { //修改
$item = Block::getById($id);
if (empty($item)) {
return $this->error('无此文字块!');
}
$categoryId = $item['category_id'];
$this->data['item'] = $item;
}
$this->data['categoryId'] = $categoryId;
return $this->view();
}
}
//富文本内容
public function text()
{
if ($this->request->isPost()) {
$item = input('post.item/a');
$categoryId = input('post.category_id/d');
$item['keyword'] = Tool::trimSpace($item['keyword']);
try {
validate(VBlock::class)->check($item);
/* 允许内容为空
if(empty(strip_tags($item['value']))){
return $this->json(1, '内容不可为空!');
}
*/
$block = Block::getByKeyword($item['keyword'], $categoryId);
$id = input('post.id/d');
if ($id) {
if (!empty($block) && $block['id'] != $id) {
return $this->json(4, '键值已存在,请更改');
}
Block::updateById($id, $item);
Log::write('page', 'text', "单页富文本编辑ID{$id} ,键值:{$item['keyword']}");
} else {
if ($categoryId <= 0) {
return $this->json(2, '栏目参数错误!');
}
if (!empty($block)) {
return $this->json(4, '键值已存在,请更改键值');
}
$item['category_id'] = $categoryId;
$item['type'] = Block::TEXT;
$block = Block::create($item);
Log::write('page', 'text', "单页富文本新增ID{$block->id} ,键值:{$item['keyword']}");
}
return $this->json();
} catch (ValidateException $e) {
return $this->json(1, $e->getError());
}
} else {
$id = input('param.id/d');
if ($id <= 0) { //添加
$categoryId = input('param.category_id/d');
$category = Category::getById($categoryId);
if (empty($category)) {
$url = url('manager.content/index')->__toString();
return $this->error('无此栏目', $url);
}
} else { //修改
$item = Block::getById($id);
if (empty($item)) {
return $this->error('无此富文本!');
}
$categoryId = $item['category_id'];
$this->data['item'] = $item;
}
$this->data['categoryId'] = $categoryId;
return $this->view();
}
}
//组图
public function group()
{
if ($this->request->isPost()) {
$item = input('post.item/a');
$imgs = input('post.img/a');
$categoryId = input('post.category_id/d');
if (!empty($imgs) && is_array($imgs)) {
$item['value'] = json_encode(array_values($imgs));
} else {
$item['value'] = '';
}
$item['keyword'] = Tool::trimSpace($item['keyword']);
try {
validate(VBlock::class)->check($item);
$block = Block::getByKeyword($item['keyword'], $categoryId);
$id = input('post.id/d');
if ($id) {
if (!empty($block) && $block['id'] != $id) {
return $this->json(4, '键值已存在,请更改');
}
Block::updateById($id, $item);
Log::write('page', 'group', "单页组图编辑ID{$id} ,键值:{$item['keyword']}");
} else {
if ($categoryId <= 0) {
return $this->json(2, '栏目参数错误!');
}
if (!empty($block)) {
return $this->json(4, '键值已存在,请更改键值');
}
$item['category_id'] = $categoryId;
$item['type'] = Block::GROUP;
$block = Block::create($item);
Log::write('page', 'group', "单页组图新增ID{$block->id} ,键值:{$item['keyword']}");
}
return $this->json();
} catch (ValidateException $e) {
return $this->json(1, $e->getError());
}
} else {
$id = input('param.id/d');
if ($id <= 0) { //添加
$categoryId = input('param.category_id/d');
$category = Category::getById($categoryId);
if (empty($category)) {
$url = url('manager.content/index')->__toString();
return $this->error('无此栏目', $url);
}
} else { //修改
$item = Block::getById($id);
if (empty($item)) {
return $this->error('无此组图!');
}
$categoryId = $item['category_id'];
$this->data['item'] = $item;
}
$this->data['categoryId'] = $categoryId;
if (isset($item) && $item['width'] && $item['height']) {
$imgSize = $item['width'] . 'px X ' . $item['height'] . 'px';
} else {
$imgSize = '';
}
$this->data['imgSize'] = $imgSize;
$this->data['groupId'] = session('auth.groupId');
return $this->view();
}
}
//视频
public function video()
{
if ($this->request->isPost()) {
$item = input('post.item/a');
$img = trim(input('post.img'));
$video = trim(input('post.video'));
$categoryId = input('post.category_id/d');
$item['keyword'] = Tool::trimSpace($item['keyword']);
try {
validate(VBlock::class)->check($item);
$block = Block::getByKeyword($item['keyword'], $categoryId);
$id = input('post.id/d');
if ($id) {
if (!empty($block) && $block['id'] != $id) {
return $this->json(4, '键值已存在,请更改');
}
if (!empty($img)) {
$item['img'] = $img;
}
if (!empty($video)) {
$item['value'] = $video;
}
Block::updateById($id, $item);
Log::write('page', 'video', "单页视频编辑ID{$id} ,键值:{$item['keyword']}");
} else {
if ($categoryId <= 0) {
return $this->json(2, '栏目参数错误!');
}
if (!empty($block)) {
return $this->json(3, '键值已存在,请更改键值');
}
if (empty($video)) {
return $this->json(3, '视频不可为空');
}
$item['category_id'] = $categoryId;
$item['type'] = Block::VIDEO;
$item['value'] = $video;
$item['img'] = $img;
$block = Block::create($item);
Log::write('page', 'video', "单页视频新增ID{$block->id} ,键值:{$item['keyword']}");
}
return $this->json();
} catch (ValidateException $e) {
return $this->json(1, $e->getError());
}
} else {
$id = input('param.id/d');
if ($id <= 0) { //添加
$categoryId = input('param.category_id/d');
$category = Category::getById($categoryId);
if (empty($category)) {
$url = url('manager.content/index')->__toString();
return $this->error('无此栏目', $url);
}
} else { //修改
$item = Block::getById($id);
if (empty($item)) {
return $this->error('无此视频!');
}
$categoryId = $item['category_id'];
$this->data['item'] = $item;
}
$this->data['categoryId'] = $categoryId;
if (isset($item) && $item['width'] && $item['height']) {
$imgSize = $item['width'] . 'px X ' . $item['height'] . 'px';
} else {
$imgSize = '';
}
$this->data['imgSize'] = $imgSize;
$this->data['groupId'] = session('auth.groupId');
return $this->view();
}
}
//音频组
public function audios()
{
if ($this->request->isPost()) {
$item = input('post.item/a');
$audios = input('post.audios/a');
$categoryId = input('post.category_id/d');
if (!empty($audios) && is_array($audios)) {
$item['value'] = json_encode(array_values($audios));
} else {
$item['value'] = '';
}
$item['keyword'] = Tool::trimSpace($item['keyword']);
try {
validate(VBlock::class)->check($item);
$block = Block::getByKeyword($item['keyword'], $categoryId);
$id = input('post.id/d');
if ($id) {
if (!empty($block) && $block['id'] != $id) {
return $this->json(4, '键值已存在,请更改');
}
Block::updateById($id, $item);
Log::write('page', 'group', "单页音频组编辑ID{$id} ,键值:{$item['keyword']}");
} else {
if ($categoryId <= 0) {
return $this->json(2, '栏目参数错误!');
}
if (!empty($block)) {
return $this->json(4, '键值已存在,请更改键值');
}
$item['category_id'] = $categoryId;
$item['type'] = Block::AUDIOS;
$block = Block::create($item);
Log::write('page', 'group', "单页音频组新增ID{$block->id} ,键值:{$item['keyword']}");
}
return $this->json();
} catch (ValidateException $e) {
return $this->json(1, $e->getError());
}
} else {
$id = input('param.id/d');
if ($id <= 0) { //添加
$categoryId = input('param.category_id/d');
$category = Category::getById($categoryId);
if (empty($category)) {
$url = url('manager.content/index')->__toString();
return $this->error('无此栏目', $url);
}
} else { //修改
$item = Block::getById($id);
if (empty($item)) {
return $this->error('无此音频组!');
}
$categoryId = $item['category_id'];
$this->data['item'] = $item;
}
$this->data['categoryId'] = $categoryId;
$this->data['groupId'] = session('auth.groupId');
return $this->view();
}
}
//视频组
public function videos()
{
if ($this->request->isPost()) {
$item = input('post.item/a');
$videos = input('post.videos/a');
$categoryId = input('post.category_id/d');
if (!empty($videos) && is_array($videos)) {
$item['value'] = json_encode(array_values($videos));
} else {
$item['value'] = '';
}
$item['keyword'] = Tool::trimSpace($item['keyword']);
try {
validate(VBlock::class)->check($item);
$block = Block::getByKeyword($item['keyword'], $categoryId);
$id = input('post.id/d');
if ($id) {
if (!empty($block) && $block['id'] != $id) {
return $this->json(4, '键值已存在,请更改');
}
Block::updateById($id, $item);
Log::write('page', 'group', "单页视频组编辑ID{$id} ,键值:{$item['keyword']}");
} else {
if ($categoryId <= 0) {
return $this->json(2, '栏目参数错误!');
}
if (!empty($block)) {
return $this->json(4, '键值已存在,请更改键值');
}
$item['category_id'] = $categoryId;
$item['type'] = Block::VIDEOS;
$block = Block::create($item);
Log::write('page', 'group', "单页视频组新增ID{$block->id} ,键值:{$item['keyword']}");
}
return $this->json();
} catch (ValidateException $e) {
return $this->json(1, $e->getError());
}
} else {
$id = input('param.id/d');
if ($id <= 0) { //添加
$categoryId = input('param.category_id/d');
$category = Category::getById($categoryId);
if (empty($category)) {
$url = url('manager.content/index')->__toString();
return $this->error('无此栏目', $url);
}
} else { //修改
$item = Block::getById($id);
if (empty($item)) {
return $this->error('无此视频组!');
}
$categoryId = $item['category_id'];
$this->data['item'] = $item;
}
$this->data['categoryId'] = $categoryId;
$this->data['groupId'] = session('auth.groupId');
return $this->view();
}
}
//地图
public function map()
{
if ($this->request->isPost()) {
$item = input('post.item/a');
$item["value"] = json_encode(input('post.value/a'), JSON_UNESCAPED_UNICODE);
$item["type"] = Block::MAP;
$categoryId = input('post.category_id/d');
try {
validate(VBlock::class)->check($item);
$block = Block::getByKeyword($item['keyword'], $categoryId);
$id = input('post.id/d');
if ($id) {
if (!empty($block) && $block['id'] != $id) {
return $this->json(4, '键值已存在,请更改');
}
Block::updateById($id, $item);
Log::write('page', 'group', "单页视频组编辑ID{$id} ,键值:{$item['keyword']}");
} else {
if ($categoryId <= 0) {
return $this->json(2, '栏目参数错误!');
}
if (!empty($block)) {
return $this->json(4, '键值已存在,请更改键值');
}
$item['category_id'] = $categoryId;
$item['type'] = Block::MAP;
$block = Block::create($item);
Log::write('page', 'group', "地图碎片新增ID{$block->id} ,键值:{$item['keyword']}");
}
return $this->json();
} catch (ValidateException $e) {
return $this->json(1, $e->getError());
}
} else {
$id = input('param.id/d');
if ($id <= 0) { //添加
$categoryId = input('param.category_id/d');
$category = Category::getById($categoryId);
if (empty($category)) {
$url = url('manager.content/index')->__toString();
return $this->error('无此栏目', $url);
}
} else { //修改
$item = Block::getById($id);
if (empty($item)) {
return $this->error('无此视地图碎片!');
}
$categoryId = $item['category_id'];
$item["value"] = json_decode($item["value"], true);
$this->data['item'] = $item;
}
$this->data['categoryId'] = $categoryId;
$this->data['groupId'] = session('auth.groupId');
$this->data['mapType'] = Block::$mapType;
return $this->view();
}
}
public function mapPlugin()
{
$data= [
"lng"=>input("lng",""),
"lat"=>input("lat",""),
];
return view("/manager/map_plugin/" . input("template", "gaode"))->assign($data);
}
}

View File

@ -0,0 +1,145 @@
<?php
namespace app\controller\manager;
use app\validate\PositionValidate;
use app\model\{PositionModel, Category as MCategory, Log as MLog, System};
use think\exception\ValidateException;
use think\response\Json;
use think\response\View;
/**
* 招聘职位
* Class Position
* @package app\controller\manager
*/
class Position extends Base
{
/**
* @return Json|View
*/
public function add()
{
if (request()->isPost()) {
$params = input('post.item/a', []);
$params['create_time'] = time();
try {
validate(PositionValidate::class)->check($params);
$newItem = PositionModel::create($params);
MLog::write('position', 'add', '新增职位ID:'.$newItem->id);
} catch (ValidateException $e) {
return $this->json(1, $e->getError());
}
return $this->json();
} else {
$categoryId = input('param.category_id/d', 0);
$category = MCategory::getById($categoryId);
$this->data['category'] = $category;
return $this->view();
}
}
/**
* @return Json|View
*/
public function edit()
{
$id = input('param.id/d', 0);
$item = PositionModel::getById($id);
if (count($item) == 0) {
return $this->json(1, '该职位不存在');
}
if (request()->isPost()) {
$params = input('post.item/a', []);
try {
validate(PositionValidate::class)->check($params);
PositionModel::updateById($id, $params);
MLog::write('position', 'edit', '修改职位ID:'.$id);
} catch (ValidateException $e) {
return $this->json(2, $e->getError());
}
return $this->json();
} else {
$this->data['item'] = $item;
return $this->view();
}
}
public function sort()
{
if (request()->isPost()) {
$id = input('post.id/d');
$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 = PositionModel::getById($id);
if (empty($item)) {
return $this->json(3, '该职位不存在');
}
if ($sort == 'up') {
$where = "category_id='{$item['category_id']}' and sort < {$item['sort']}";
$order = "sort desc";
} else {
$where = "category_id='{$item['category_id']}' and sort > {$item['sort']}";
$order = "sort asc";
}
$forSortItems = PositionModel::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 PositionModel();
$model->saveAll($updateData);
$sortStr = $sort == 'up' ? '上移' : '下调';
MLog::write('position', 'sort', "职位排序ID{$id} {$sortStr}{$num}");
return $this->json();
}
}
return $this->json(4, '无须调整排序!');
}
return $this->json(1, '无此操作');
}
// 删除
public function del()
{
if (request()->isPost()) {
$historyId = input('param.id/d', 0);
$item = PositionModel::getById($historyId);
if (count($item) == 0) {
return $this->json(2, '该职位不存在');
}
try {
PositionModel::destroy($historyId);
MLog::write('position', 'del', '删除职位ID:'.$historyId);
} catch (\Exception $e) {
return $this->json(3, '删除失败,'.$e->getMessage());
}
return $this->json();
}
return $this->json(1, '无此操作');
}
}

View File

@ -0,0 +1,167 @@
<?php
namespace app\controller\manager;
use app\validate\ProductValidate;
use app\model\{ProductModel, Category as MCategory, Log as MLog, System};
use think\exception\ValidateException;
use think\response\Json;
use think\response\View;
/**
* 产品
* Class Honour
* @package app\controller\manager
*/
class Product extends Base
{
/**
* @return Json|View
*/
public function add()
{
if (request()->isPost()) {
$images = input('img_images/a');
$params = input('post.item/a', []);
$params['image'] = input('img');
$params['images'] = json_encode($images, JSON_UNESCAPED_UNICODE);
$params['qr'] = input('img_qr');
try {
validate(ProductValidate::class)->check($params);
$newItem = ProductModel::create($params);
MLog::write('honour', 'add', '新增产品ID:'.$newItem->id);
} catch (ValidateException $e) {
return $this->json(1, $e->getError());
}
return $this->json();
} else {
$categoryId = input('param.category_id/d', 0);
$category = MCategory::getById($categoryId);
if (count($category) > 0 && $category['img_width'] && $category['img_height']) {
$imgSize = $category['img_width'].'像素 X '.$category['img_height'].'像素';
} else {
$imgSize = System::getArticleImageSize();
}
$this->data['imgSize'] = $imgSize;
$this->data['category'] = $category;
return $this->view();
}
}
/**
* @return Json|View
*/
public function edit()
{
$id = input('param.id/d', 0);
$item = ProductModel::getById($id);
if (count($item) == 0) {
return $this->json(1, '该产品不存在');
}
if (request()->isPost()) {
$params = input('post.item/a', []);
$params['image'] = input('img');
$params['qr'] = input('img_qr');
$params['images'] = json_encode(array_values(input('img_images/a')), JSON_UNESCAPED_UNICODE);
try {
validate(ProductValidate::class)->check($params);
ProductModel::updateById($id, $params);
MLog::write('honour', 'edit', '修改产品ID:'.$id);
} catch (ValidateException $e) {
return $this->json(2, $e->getError());
}
return $this->json();
} else {
$category = MCategory::getById($item['category_id']);
if (count($category) > 0 && $category['img_width'] && $category['img_height']) {
$imgSize = $category['img_width'].'像素 X '.$category['img_height'].'像素';
} else {
$imgSize = System::getArticleImageSize();
}
$this->data['imgSize'] = $imgSize;
$this->data['item'] = $item;
return $this->view();
}
}
public function sort()
{
if (request()->isPost()) {
$id = input('post.id/d');
$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 = ProductModel::getById($id);
if (empty($item)) {
return $this->json(3, '该产品不存在');
}
if ($sort == 'up') {
$where = "category_id='{$item['category_id']}' and sort < {$item['sort']}";
$order = "sort desc";
} else {
$where = "category_id='{$item['category_id']}' and sort > {$item['sort']}";
$order = "sort asc";
}
$forSortItems = ProductModel::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 ProductModel();
$model->saveAll($updateData);
$sortStr = $sort == 'up' ? '上移' : '下调';
MLog::write('history', 'sort', "产品排序ID{$id} {$sortStr}{$num}");
return $this->json();
}
}
return $this->json(4, '无须调整排序!');
}
return $this->json(1, '无此操作');
}
// 删除
public function del()
{
if (request()->isPost()) {
$historyId = input('param.id/d', 0);
$item = ProductModel::getById($historyId);
if (count($item) == 0) {
return $this->json(2, '该产品不存在');
}
try {
ProductModel::destroy($historyId);
MLog::write('honour', 'del', '删除产品ID:'.$historyId);
} catch (\Exception $e) {
return $this->json(3, '删除失败,'.$e->getMessage());
}
return $this->json();
}
return $this->json(1, '无此操作');
}
}

181
app/controller/manager/Rule.php Executable file
View File

@ -0,0 +1,181 @@
<?php
namespace app\controller\manager;
use app\model\{AuthRule, AuthGroup, Log};
use app\validate\AuthRule as VAuthRule;
use think\exception\ValidateException;
class Rule extends Base
{
/**
* 权限排序
* 暂不允许父级变更
*
* @return void
*/
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' ? '上移' : '下调';
AuthGroup::resetGroupRulesCache();
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()) {
$id = input('post.id/d');
$item = AuthRule::getById($id);
if(empty($item)){
return $this->json(1, '无此权限');
}
$children = AuthRule::getListByParentId($id);
if(!empty($children)){
return $this->json(2, '当前权限有下级权限,不可删除');
}
AuthRule::destroy($id);
AuthGroup::resetGroupRulesCache();
Log::write('rule', 'del', "权限删除ID{$id}, 标题:{$item['title']}");
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();
}
}

70
app/controller/manager/Safe.php Executable file
View File

@ -0,0 +1,70 @@
<?php
namespace app\controller\manager;
use app\service\Jwt;
use app\model\{Member, Log};
use think\response\Json;
use think\response\View;
class Safe extends Base
{
/**
* 安全设置
* @return View|Json
*/
public function index()
{
$auth = session('auth');
if($this->request->isPost()){
if ($auth) {
$authId = $auth['userId'];
$oldPassword = trim(input('post.password_old'));
$password = trim(input('post.password'));
$passwordAgain = trim(input('post.password_again'));
$name = trim(input('post.name'));
$user = Member::getByID($authId);
if (empty($user)) {
return $this->json(1, '登录失效,请重新登录后再试!');
}
if (empty($name)) {
return $this->json(2, '用户名不能为空!');
}
$hasUser = Member::getByUserName($name);
if (!empty($hasUser) && $hasUser['id'] != $authId) {
return $this->json(3, '该用户名已被其他用户使用,请更换!');
}
if (empty($password) || empty($oldPassword)) {
return $this->json(4, '用户密码不能为空!');
}
if ($password != $passwordAgain) {
return $this->json(5, '新密码两次输入不一致!');
}
if (mb_strlen($password) < 6 || mb_strlen($password) > 30) {
return $this->json(6, '新密码长度格式不正确请输入6~30位密码');
}
if ($user['password'] != md5($oldPassword)) {
return $this->json(7,'原密码不正确');
}
$data['password'] = md5($password);
Member::updateById($authId, $data);
Log::write('safe', 'index', "安全设置ID{$authId}, 管理员:{$name}");
session('auth', null);
//cache('rules_'.$authId, null); //当前看代码,这个是无用代码;先注释掉,如果在使用过程中不会用到,再删除。
cache('group_rules_'.$authId, null);
cache('rule_names_'.$authId, null);
return $this->json(0, '修改成功,请重新登录!');
} else {
return $this->json(1, '登录失效,请重新登录后再试!');
}
}else{
$this->data['item'] = $auth;
$jwtData = [
'member_id' => $auth["userId"],
];
$this->data["token"] = Jwt::generate($jwtData);
return $this->view();
}
}
}

190
app/controller/manager/Slide.php Executable file
View File

@ -0,0 +1,190 @@
<?php
namespace app\controller\manager;
use app\model\{Slide as MSlide, System, Log};
use app\validate\Slide as VSlide;
use think\exception\ValidateException;
class Slide extends Base
{
//批量删除
public function batchDel()
{
if ($this->request->isPost()) {
$ids = input('post.ids/a');
if(empty($ids) || !is_array($ids)) {
return $this->json(2, '参数错误,请核对之后再操作!');
}
$items = MSlide::getListByIds($ids);
if(!empty($items)){
$delIds = [];
foreach($items as $item){
$delIds[] = $item['id'];
}
MSlide::destroy($delIds);
Log::write('link', 'betchDel', '批量删除了友情链接涉及到的ID为' . implode(',', $delIds));
return $this->json();
}else{
return $this->json(3, '待删除友情链接为空');
}
}
return $this->json(1, '非法请求!');
}
//可以删除一个可以批量删除TODO 需要调整
public function del()
{
if ($this->request->isPost()) {
$id = input('post.id/d');
if(is_numeric($id) && $id > 0) {
$item = MSlide::getById($id);
if(!empty($item)){
MSlide::destroy($id);
Log::write('link', 'del', '删除轮播图ID' . $id . ',标题:' . $item['title']);
return $this->json();
}
return $this->json(3, '待删除轮播图不存在');
}
return $this->json(2, '参数错误,请核对之后再操作!');
}
return $this->json(1, '非法请求!');
}
//排序
public function sort()
{
if($this->request->isPost()){
$id = input('post.id/d');
$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 = MSlide::getById($id);
if(empty($item)){
return $this->json(3, '无此轮播图');
}
if($sort == 'up'){
$where = "sort < {$item['sort']}";
$order = "sort desc";
}else{
$where = "sort > {$item['sort']}";
$order = "sort asc";
}
$forSortItems = MSlide::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 MSlide();
$model->saveAll($updateData);
$sortStr = $sort == 'up' ? '上移' : '下调';
Log::write('slide', 'sort', "轮播图排序ID{$id} ,标题:{$item['title']}{$sortStr}{$num}");
return $this->json();
}
}
return $this->json(4, '无须调整排序!');
}
return $this->json(1, '非法请求!');
}
//编辑
public function edit()
{
if($this->request->isPost()){
$item = input('post.item');
$img = input('post.img');
$img_mobile = input('post.img_mobile');
$id = input('post.id/d');
if(is_numeric($id) && $id > 0) {
$slide = MSlide::getById($id);
if(empty($slide)) {
return $this->json(2, 'id参数错误,没有相关轮播图信息记录!');
}
if(!empty($img)){
$item['src'] = $img;
}
if(!empty($img_mobile)){
$item['src_mobile'] = $img_mobile;
}
try {
validate(VSlide::class)->check($item);
MSlide::updateById($id, $item);
Log::write('slide', '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 = MSlide::getById($id);
$imgSize = System::getSlideImageSize();
$this->data['item'] = $item;
$this->data['img_size'] = $imgSize;
return $this->view();
}
}
//添加
public function add()
{
if($this->request->isPost()){
$item = input('post.item');
$img = input('post.img');
$img_mobile = input('post.img_mobile');
if (empty($item)) {
return $this->json(1, '参数错误,请核对之后再操作!');
}
if(!empty($img)){
$item['src'] = $img;
}
if(!empty($img_mobile)){
$item['src_mobile'] = $img_mobile;
}
try {
validate(VSlide::class)->check($item);
$slide = MSlide::create($item);
Log::write('slide', 'add', "轮播图新增ID{$slide->id} ,标题:{$item['title']}");
return $this->json();
} catch (ValidateException $e) {
return $this->json(2,$e->getError());
}
}else{
$this->data['img_size'] = System::getSlideImageSize();
return $this->view();
}
}
/**
* 列表
* 暂定只有首页轮播图,后期可以根据需求进行分组管理
* @return Slide
*/
public function index()
{
$items = MSlide::getList();
$this->data['items'] = $items;
return $this->view();
}
}

View File

@ -0,0 +1,63 @@
<?php
namespace app\controller\manager;
use app\service\Tool;
use app\model\{System as MSystem, Log};
use app\service\Image;
use think\facade\Cache;
class System extends Base
{
/**
* 获取当前系统设置
*
* @return void
*/
public function index()
{
if ($this->request->isPost()) {
$item = input('post.item/a');
$img = input('post.img');
$system = MSystem::getSystem();
if (empty($system)) {
if(!empty($img)){
$item['mark_img'] = $img;
}
$system = MSystem::create($item);
Log::write('system', 'index', "系统设置ID{$system->id}");
} else {
if (!empty($img)) {
Image::delImg($system['mark_img']);
$item['mark_img'] = $img;
}
MSystem::update($item, ['id' => $system['id']]);
Log::write('system', 'index', "系统设置ID{$system['id']}");
}
return $this->json();
} else {
$item = MSystem::getSystem();
$positions = Image::getMarkPosition();
$this->data['item'] = $item;
$this->data['positions'] = $positions;
return $this->view();
}
}
public function other()
{
return $this->view();
}
public function clearCache()
{
Cache::clear();
$cachePath = app()->getRuntimePath().'cache';
$tempPath = app()->getRuntimePath().'temp';
Tool::removeByPath($cachePath);
Tool::removeByPath($tempPath);
clearstatcache();
return $this->json();
}
}

64
app/controller/manager/Tag.php Executable file
View File

@ -0,0 +1,64 @@
<?php
namespace app\controller\manager;
use app\model\ArticleTags;
use think\Exception;
use think\exception\ValidateException;
/**
* 标签管理
*
* Class Tag
* @package app\controller\manager
*/
class Tag extends Base
{
public function index()
{
$whereMap = [];
$paginateRows = [
'list_rows' => 30,
'query' => [],
];
$this->data['items'] = ArticleTags::getListWithPaginate($whereMap, $paginateRows, false);
return $this->view();
}
public function edit()
{
$id = $this->request->param('id/d', 0);
$tagInfo = ArticleTags::findInfoById($id);
if (empty($tagInfo)) {
return $this->json(4004, '没有相关的记录');
}
if ($this->request->isPost()) {
$itemData = $this->request->param('item/a', []);
if (isset($itemData['id'])) {
unset($itemData['id']);
}
try {
$this->validate($itemData, [
'seo_title|SEO标题' => 'max:100',
'seo_keyword|SEO关键词' => 'max:200',
'seo_description|SEO描述' => 'max:200',
]);
$tagInfo->save($itemData);
} catch (ValidateException $e) {
return $this->json(4001, $e->getError());
} catch (Exception $e) {
return $this->json(5001, '系统繁忙,修改失败!');
}
return $this->json(0, '修改成功!');
}
$this->data['id'] = $id;
$this->data['item'] = $tagInfo;
return $this->view();
}
}

211
app/controller/manager/Upload.php Executable file
View File

@ -0,0 +1,211 @@
<?php
namespace app\controller\manager;
use app\service\Image;
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;
use think\image\Exception as ImageException;
class Upload extends BaseController
{
private $isCompress = true;
private $validate;
private $uploadPath;
private $uploadPathIsWritable = 0;
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');
if(is_writable(app()->getRootPath() . 'public' . $this->uploadPath)){
$this->uploadPathIsWritable = 1;
}
}
//视频上传
public function video()
{
if(!$this->uploadPathIsWritable){
return $this->json(1, '上传文件夹需要写入权限');
}
$video = request()->file('video');
if($this->validate->checkVideo($video)){
$src = Filesystem::disk('video')->putFile(date('Ymd'), $video, 'uniqid');
$src = $this->uploadPath . '/' . $src;
$return['src'] = $src;
//加入上传文件表
$newFile = File::add($video, $src, 'video');
$return['fid'] = $newFile->id;
return $this->json(0, 'ok', $return);
}else{
$errorMsg = Lang::get($this->validate->getError());
return $this->json(1, $errorMsg);
}
}
//音频上传
public function audio()
{
if(!$this->uploadPathIsWritable){
return $this->json(1, '上传文件夹需要写入权限');
}
$audio = request()->file('audio');
if($this->validate->checkFile($audio)){
$src = Filesystem::disk('audio')->putFile(date('Ymd'), $audio, 'uniqid');
$src = $this->uploadPath . '/' . $src;
$return['src'] = $src;
File::add($audio, $src, 'audio'); //加入上传文件表
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');
if($this->validate->checkFile($file)){
try{
if(!$this->uploadPathIsWritable){
throw new \Exception('上传文件夹需要写入权限');
}
$src = Filesystem::putFile(date('Ymd'), $file, 'uniqid');
$src = $this->uploadPath . '/' . $src;
$return['src'] = $src;
$return['name'] = $file->getOriginalName();
File::add($file, $src, '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 = request()->file('image');
// $md5 = $image->md5();//文件md5
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(0, '该文件已存在 路径为:'.$fileItem['src'], $return);
// }
try{
if(!$this->uploadPathIsWritable){
throw new \Exception('上传文件夹需要写入权限');
}
$src = Filesystem::putFile(date('Ymd'), $image, 'uniqid');
$src = $this->uploadPath . '/' . $src;
// $suffix = strtolower($image->getOriginalExtension());
// if($suffix == 'gif'){
// $return['thumb_src'] = $src; //TODO获取GIF缩略图
// }else{
// $return['thumb_src'] = Image::getThumb($src, 300, 300, TImage::THUMB_SCALING); //上传返回缩略图宽度为300
// }
$return['src'] = $src;
// if($this->isCompress){
// Image::resize($src);
// }
File::add($image, $src); //加入上传文件表
} catch (\Exception|ImageException $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) {
if($this->validate->checkImage($image)){
$src = Filesystem::putFile(date('Ymd'), $image, 'uniqid');
$src = $this->uploadPath . '/' . $src;
$data[] = $src;
if($this->isCompress){
Image::resize($src);
}
File::add($image, $src); //加入上传文件表
}else{
$errno = 1;
$data = [];
$data[] = Lang::get($this->validate->getError());
break;
}
}
}
$return['errno'] = $errno;
$return['data'] = $data;
return json($return);
}
//富文本编辑器上传图片
public function tinyImage()
{
$image = request()->file('file');
if (!$image) {
// 字段名不对
header("HTTP/1.1 404 config field error");
exit;
}
if (!$this->uploadPathIsWritable) {
header("HTTP/1.1 403 Insufficient folder permissions");
exit;
} else {
if (!in_array(strtolower($image->getMime()), ['image/png','image/jpeg','image/jpg','image/gif'])) {
header("HTTP/1.1 400 MIME TYPE ERROR");
exit;
}
checkPathExistWithMake(public_path().$this->uploadPath.'/images/'.date('Ym'));
// tinymce富文本对图片进行操作后xuan上传的是blob文件。
// 针对blob文件未读取到后缀名 自动生成后缀 默认用mimetype后缀 如image/jpeg =》jpeg
$newFileName = $image->hashName('uniqid');
if (isset(explode('.',$newFileName)[1]) && empty(explode('.',$newFileName)[1])) {
$ext = explode('/', $image->getOriginalMime());
$newFileName .= $ext[1];
}
$src = Filesystem::putFileAs('images/'.date('Ym'), $image, $newFileName);
$src = $this->uploadPath.'/'.$src;
if ($this->isCompress) {
// 剪切
Image::resize($src);
}
File::add($image, $src); //加入上传文件表
$res['location'] = $src;
return json($res);
}
}
}

View File

@ -0,0 +1,147 @@
<?php
namespace app\controller\manager;
use app\model\{Message as MMessage, Log, VisitLogoModel};
use app\service\Tool;
use app\service\File;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\Border;
use think\response\View;
class VisitLog extends Base
{
protected $excelStyle = [
'font' => [
'name' => '宋体',
],
'alignment' => [
'horizontal' => Alignment::HORIZONTAL_CENTER, // 水平居中
'vertical' => Alignment::VERTICAL_CENTER, // 垂直居中
'wrapText' => true,
],
'borders' => [
'allBorders' => [
'borderStyle' => Border::BORDER_THIN,
'color' => ['rgb' => 'eeeeee'],
]
],
];
protected $defaultSetting = [
'cell_width' => 30,
'font_size' => 12
];
//可以删除一个
public function del()
{
if ($this->request->isPost()) {
$id = input('post.id/d');
if (is_numeric($id) && $id > 0) {
$item = VisitLogoModel::getById($id);
if (!empty($item)) {
VisitLogoModel::destroy($id);
if (!empty($item['file'])) {
Tool::delFile($item['file']);
}
Log::write('visit_log', 'del', '删除访问记录ID'.$id);
return $this->json();
}
return $this->json(3, '待删除记录不存在');
}
return $this->json(2, '参数错误,请核对之后再操作!');
}
return $this->json(1, '非法请求!');
}
//列表
public function index(): View
{
$startDate = input('param.startDate', '');
$endDate = input('param.endDate', '');
$keyword = input('param.keyword', '');
$param = [];
if (!empty($startDate)) {
$param['startDate'] = $startDate;
}
if (!empty($endDate)) {
$param['endDate'] = $endDate;
}
if (!empty($keyword)) {
$param['keyword'] = $keyword;
}
$paginate = [
'list_rows' => 20,
'query' => $param
];
$items = VisitLogoModel::when(!empty($startDate) && strtotime($startDate), function ($query) use ($startDate) {
$startTime = strtotime(date('Y-m-d 00:00:00', strtotime($startDate)));
$query->where('create_time', '>=', $startTime);
})
->when(!empty($endDate) && strtotime($endDate), function ($query) use ($endDate) {
$endTime = strtotime(date('Y-m-d 23:59:59', strtotime($endDate)));
$query->where('create_time', '<=', $endTime);
})
->when(!empty($keyword), function ($query) use ($keyword) {
$query->where('referer|visit', 'like', '%'.$keyword.'%');
})
->order("create_time", 'desc')
->paginate($paginate);
$items->each(function ($item) {
$item->source_title = '其他';
if (str_contains($item->referer, 'baidu.com')) {
$item->source_title = '百度';
}
if (str_contains($item->referer, 'so.com')) {
$item->source_title = '360';
}
});
$this->data['items'] = $items;
$this->data['startDate'] = $startDate;
$this->data['endDate'] = $endDate;
$this->data['keyword'] = $keyword;
return $this->view();
}
// 导出留言
public function export()
{
File::cancelTimeLimit();
$startDate = input('param.startDate', '');
$endDate = input('param.endDate', '');
$list = MMessage::getExportList($startDate, $endDate, 10000);
$spreadsheet = new Spreadsheet();
$header = ['序号', '姓名', '电话', '公司/团队名称', '申请日期'];
$sheet = $spreadsheet->getActiveSheet();
$sheetTitle = '预约记录';
$cellValues = [];
$cellWidthList = [];
foreach ($list as $item) {
$cellValues[] = [
[$item['id'], DataType::TYPE_STRING],
$item['name'],
$item['tel'],
$item['company'],
date('Y-m-d H:i', $item['create_time']),
];
}
File::setExcelCells($sheet, $cellValues, $header, $sheetTitle, $cellWidthList, $this->excelStyle, $this->defaultSetting);
File::export($spreadsheet, '预约记录导出_'.date('YmdHis').'.xlsx');
}
}

View File

@ -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 ...';
}
}

17
app/event.php Executable file
View File

@ -0,0 +1,17 @@
<?php
// 事件定义文件
return [
'bind' => [
],
'listen' => [
'AppInit' => [],
'HttpRun' => [],
'HttpEnd' => [],
'LogLevel' => [],
'LogWrite' => [],
],
'subscribe' => [
],
];

10
app/middleware.php Executable file
View File

@ -0,0 +1,10 @@
<?php
// 全局中间件定义文件
return [
// 全局请求缓存
// \think\middleware\CheckRequestCache::class,
// 多语言加载
//\think\middleware\LoadLangPack::class,
// Session初始化
\think\middleware\SessionInit::class,
];

65
app/middleware/Auth.php Executable file
View File

@ -0,0 +1,65 @@
<?php
namespace app\middleware;
use Closure;
use app\model\AuthRule;
use think\facade\Cache;
class Auth
{
public function handle($request, Closure $next)
{
$auth = session('auth');
if(!$auth){
return redirect(url('manager.login/index'));
}
// 角色权限
$rules = Cache::get('group_rules_'.$auth['groupId']);
$ruleNames = Cache::get('rule_names_'.$auth['groupId']);
//如果是超级管理员,不用验证权限,给予所有权限
if(empty($rules)){
$ruleNames = [];
if($auth['groupId'] == 1){
$rules = AuthRule::getListTree(0);
}else{
// 角色权限 + 基本权限
$rules = AuthRule::getAuthListByRuleIDs($auth['groupId']);
}
foreach($rules as &$rule){
if(!stripos($rule['name'],'/')){
$rule['name'] = $rule['name'].'/index';
}
$ruleNames[] = strtolower($rule['name']);
if(isset($rule['children']) && !empty($rule['children'])){
foreach($rule['children'] as &$child){
if(!stripos($child['name'],'/')){
$child['name'] = $child['name'].'/index';
}
$ruleNames[] = strtolower($child['name']);
}
}
}
// 对角色赋予权限缓存,角色权限更新时需要同步更新缓存
Cache::set('group_rules_'.$auth['groupId'], $rules);
Cache::set('rule_names_'.$auth['groupId'], $ruleNames);
}
if($auth['groupId'] == 1){
return $next($request);
}
$controller = strtolower(request()->controller());
$controller = str_replace('manager.', '', $controller);
$action = request()->action();
$name = strtolower($controller.'/'.$action);
if(!empty($ruleNames) && in_array($name, $ruleNames, true)){
return $next($request);
}
if(request()->isAjax()){
return json(['code' => 1,'msg' => '没有权限']);
}else{
exit('无操作权限') ;
}
}
}

55
app/middleware/Csrf.php Executable file
View File

@ -0,0 +1,55 @@
<?php
namespace app\middleware;
use Closure;
/**
* CSRF校验
*/
class Csrf
{
public function handle($request, Closure $next)
{
if($request->isPost()){
$check = $request->checkToken('__token__');
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
}
}
}

555
app/model/Article.php Executable file
View File

@ -0,0 +1,555 @@
<?php
namespace app\model;
use think\model\relation\HasOne;
use think\Paginator;
class Article extends Base
{
public const STATUS_NORMAL = 1; // 正常
public const STATUS_DISABLE = 0; // 禁用
// 文章属性(默认)
protected static $defaultAttributeList = [
'top' => '置顶',
'hot' => '热门',
'recommend' => '推荐',
];
/**
* 栏目
* @return HasOne
*/
public function archivesCategory(): HasOne
{
return $this->hasOne(Category::class, 'id', 'category_id');
}
public static function getAttributeList(array $categoryIds = [])
{
$data = [];
$recommendCategoryList = [];
if(count(array_intersect($categoryIds, $recommendCategoryList)) > 0) {
$data['recommend'] = '推荐';
}
// 新闻动态
$communityCategoryIds = Category::getCategoryWithChildrenIds(Category::CATEGORY_NEWS);
if(count(array_intersect($categoryIds, $communityCategoryIds)) > 0) {
$data['top'] = '置顶';
$data['hot'] = '热门';
$data['recommend'] = '推荐';
}
return $data;
}
/*********************************************
* 分割线
*********************************************/
//获取最高访问的文章列表
public static function getMostVisited($limit = 5)
{
if ($limit <= 0) {
$limit = 5;
}
return self::with(["archivesCategory"])
->order('views', 'desc')
->limit($limit)
->select();
}
//获取栏目下最新记录
public static function getLatestByCategory($categoryId, $limit = 5)
{
if (empty($categoryId)) {
return [];
}
if ($limit <= 0) {
$limit = 5;
}
return self::with(["archivesCategory"])
->where('category_id', $categoryId)
->order('id', 'desc')
->limit($limit)
->select();
}
//根据文章ID和栏目ID获取下一篇文章
public static function getNextArticleBySortAndCategoryId($sort, $categoryId)
{
return self::with(["archivesCategory"])
->where('sort', '<', $sort)
->where('category_id', $categoryId)
->where('status', 1)
->order('sort', 'desc')
->find();
}
//根据文章ID和栏目ID获取上一篇文章
public static function getPrevArticleBySortAndCategoryId($sort, $categoryId)
{
return self::with(["archivesCategory"])
->where('sort', '>', $sort)
->where('category_id', $categoryId)
->where('status', 1)
->order('sort', 'asc')
->find();
}
//根据栏目ID获取文章列表
public static function getListByCategory($categoryId, $limit = 10)
{
return self::with(["archivesCategory"])
->where('category_id', $categoryId)
->where('status', 1)
->order("sort", 'desc')
->limit($limit)
->select();
}
//根据栏目ID获取文章分页列表
public static function getListPageByCategory($categoryId, $per = 20, $keyword = '')
{
$where = [
['category_id', '=', $categoryId],
['status', '=', 1],
];
$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);
}
//根据栏目ID获取文章数量状态正常
public static function getNormalListCount($categoryId)
{
$where = [
['category_id', '=', $categoryId],
['status', '=', 1],
];
return self::where($where)
->count();
}
public static function onAfterInsert($article)
{
$article->sort = $article->id;
$article->create_time = $article->update_time = time();
$auth = session('auth');
$article->created = $article->updated = $auth['userName'];
$article->save();
}
/**
* 获取文章列表
* @param int $categoryId 分类ID
* @param int $per 每页数量
* @param string $keyword 关键词
* @param array $param 文章类型:置顶、热门、推荐 ['top','hot','recommend']
* @param int $status 文章状态,-1表示不限制
* @param array $orderList 排序
* @param bool $onlyChild 仅获取下级 默认true false=获取所有后代分类
* @return Paginator
*/
public static function getList($categoryId, $per = 20, $keyword = '', $param = [], $status = -1, $orderList = ['sort' => 'desc'], bool $onlyChild = true)
{
$whereMap = [];
$pageParam = [];
if (is_numeric($categoryId) && $categoryId > 0) {
$children = Category::getChildrenByParentId($categoryId, $onlyChild);
if (!empty($children)) {
$categoryIds = [$categoryId];
foreach ($children as $child) {
if ($child['model_id'] == Model::MODEL_ARTICLE) {
$categoryIds[] = $child['id'];
}
}
$whereMap[] = ['category_id', 'in', $categoryIds];
} else {
$whereMap[] = ['category_id', '=', $categoryId];
}
$pageParam['category_id'] = $categoryId;
}
if (!empty($keyword)) {
$whereMap[] = ['title', 'like', '%'.$keyword.'%'];
$pageParam['keyword'] = $keyword;
}
if (is_array($param) && count($param) > 0) {
$pageParam['param'] = $param;
foreach ($param as $vo) {
if (in_array($vo, ['top', 'hot', 'recommend'], true)) {
$whereMap[] = ["{$vo}", '=', 1];
}
}
}
$paginate = [
'list_rows' => $per,
'query' => $pageParam
];
return self::with(["archivesCategory"])
->when(count($whereMap) > 0, function ($query) use ($whereMap) {
$query->where($whereMap);
})
->when($status != -1, function ($query) use ($status) {
$query->where('status', $status);
})
->order($orderList)
->paginate($paginate, false);
}
//获取文章涉及到的图片
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;
}
$imgs = getImageUrlFromText($item['content']);
if (!empty($imgs)) {
$data = array_merge($data, $imgs);
}
$videos = getVideoUrlFromText($item['content']);
if (!empty($videos)) {
$data = array_merge($data, $videos);
}
}
return $data;
}
//推荐列表(其他推荐类)
public static function getRecommendList($categoryId, $recommend = '', $limit = 3, $excludeIds = [], $orderList = ['a.id' => 'desc'])
{
if (empty($categoryId) || empty($recommend)) {
return [];
}
$whereMap = [];
if (is_numeric($categoryId) && $categoryId > 0) {
$children = Category::getChildrenByParentId($categoryId);
if (!empty($children)) {
$categoryIds = [$categoryId];
foreach ($children as $child) {
if ($child['model_id'] == Model::MODEL_ARTICLE) {
$categoryIds[] = $child['id'];
}
}
$whereMap[] = ['category_id', 'in', $categoryIds];
} else {
$whereMap[] = ['category_id', '=', $categoryId];
}
}
if (!empty($excludeIds)) {
$whereMap[] = ['id', 'not in', $excludeIds];
}
return self::with(["archivesCategory"])
->when(count($whereMap) > 0, function ($query) use ($whereMap) {
$query->where($whereMap);
})
->whereRaw("FIND_IN_SET(:recommend, `recommend_other`)", ['recommend' => $recommend])
->where('status', 1)
->order($orderList)
->limit($limit)
->select();
}
// 转换文章的推荐设置
public static function convertRecommendOther($categoryIds = [], $articles, $isMulti = true)
{
if (empty($articles) || count($articles) == 0) {
return $articles;
}
$attributeList = self::getAttributeList($categoryIds);
if ($isMulti) {
foreach ($articles as &$article) {
$recommendOtherList = [];
$recommendOtherStrList = [];
if (isset($article['recommend_other']) && !empty($article['recommend_other'])) {
$recommendOtherList = explode(',', $article['recommend_other']);
foreach ($recommendOtherList as $recommendKey) {
if (isset($attributeList[$recommendKey]) && !empty($attributeList[$recommendKey])) {
$recommendOtherStrList[] = $attributeList[$recommendKey];
}
}
}
$article['recommend_other_list'] = $recommendOtherList;
$article['recommend_other_str'] = implode(',', $recommendOtherStrList);
}
unset($article);
} else {
$recommendOtherList = [];
$recommendOtherStrList = [];
if (isset($articles['recommend_other']) && !empty($articles['recommend_other'])) {
$recommendOtherList = explode(',', $articles['recommend_other']);
foreach ($recommendOtherList as $recommendKey) {
if (isset($attributeList[$recommendKey]) && !empty($attributeList[$recommendKey])) {
$recommendOtherStrList[] = $attributeList[$recommendKey];
}
}
}
$articles['recommend_other_list'] = $recommendOtherList;
$articles['recommend_other_str'] = implode(',', $recommendOtherStrList);
}
return $articles;
}
// 获取最新动态
public static function getLastDynamicsList($categoryId, $withChild = false, $limit = 10)
{
$categoryIds = [$categoryId];
if ($withChild) {
$childCategories = Category::getChildrenByParentId($categoryId);
if (!empty($childCategories)) {
foreach ($childCategories as $category) {
if ($category['model_id'] == Model::MODEL_ARTICLE) {
$categoryIds[] = $category['id'];
}
}
}
}
return self::with(["archivesCategory"])
->whereIn('category_id', $categoryIds)
->where('status', 1)
->order('id', 'desc')
->limit($limit)
->select();
}
//根据文章ID和栏目IDs默认按创建时间获取下一篇文章
public static function getNextArticleByTimeAndCategoryIds(array $where, $sortOrder = ['id' => 'desc'])
{
return self::with(["archivesCategory"])
->where($where)
->where('status', 1)
->order($sortOrder)
->find();
}
//根据文章ID和栏目IDs默认按创建时间获取上一篇文章
public static function getPrevArticleByTimeAndCategoryIds(array $where, $sortOrder = ['id' => 'asc'])
{
return self::with(["archivesCategory"])
// ->where('a.id','>',$curId)
// ->whereIn('a.category_id', $categoryIds)
->where($where)
->where('status', 1)
->order($sortOrder)
->find();
}
public static function getLastTopList($categoryId, $limit = 3, $excludeIds = [], $orderList = ['recommend_other' => 'desc', 'id' => 'desc'])
{
if (empty($categoryId)) {
return [];
}
$whereMap = [];
if (is_numeric($categoryId) && $categoryId > 0) {
$children = Category::getChildrenByParentId($categoryId);
if (!empty($children)) {
$categoryIds = [$categoryId];
foreach ($children as $child) {
if ($child['model_id'] == Model::MODEL_ARTICLE) {
$categoryIds[] = $child['id'];
}
}
$whereMap[] = ['category_id', 'in', $categoryIds];
} else {
$whereMap[] = ['category_id', '=', $categoryId];
}
}
if (!empty($excludeIds)) {
$whereMap[] = ['id', 'not in', $excludeIds];
}
$items = self::with(["archivesCategory"])
->when(count($whereMap) > 0, function ($query) use ($whereMap) {
$query->where($whereMap);
})
->where('status', 1)
->order($orderList);
if ($limit > 0) {
$items = $items->limit($limit);
}
$items = $items->limit($limit)
->select();
return $items;
}
// 分页获取最新动态
public static function getLastDynamicsPageList($categoryId, $withChild = true, $per = 20)
{
$categoryIds = [$categoryId];
if ($withChild) {
$childCategories = Category::getChildrenByParentId($categoryId);
if (!empty($childCategories)) {
foreach ($childCategories as $category) {
if ($category['model_id'] == Model::MODEL_ARTICLE) {
$categoryIds[] = $category['id'];
}
}
}
}
$paginate = [
'list_rows' => $per,
'query' => [
'category_id' => $categoryId
]
];
return self::with(["archivesCategory"])
->whereIn('category_id', $categoryIds)
->where('status', 1)
->order('id', 'desc')
->paginate($paginate, false);
}
// 根据栏目ID进行分组按sort倒序排列每组最多获取n条(默认10条)数据
public static function getGroupListByCategoryIds(array $categoryIds, int $size = 10)
{
return self::alias('ca')
->where($size, '>', function ($q) {
$q->table('bee_article cb')
->whereRaw('ca.category_id = cb.category_id and ca.sort < cb.sort')->field('count(*)');
})
->whereIn('ca.category_id', $categoryIds)
->field('ca.*')
->order(['ca.sort' => 'desc'])
->select();
}
public static function parseList($items)
{
try {
$tagNameList = [];
foreach ($items as $ki => $item) {
$tagStr = trim($item['tag'] ?? '');
$tagNames = empty($tagStr) ? [] : explode(',', $tagStr);
$tagNameList = array_merge($tagNameList, $tagNames);
}
$tagNameList = array_unique($tagNameList);
$tagKVs = ArticleTags::findByNames($tagNameList)->column('id', 'name');
foreach ($items as $ki => $item) {
$tagStr = trim($item['tag'] ?? '');
$tagNames = empty($tagStr) ? [] : explode(',', $tagStr);
$tagList = [];
foreach ($tagNames as $tagName) {
$tagList[] = [
'name' => $tagName,
'id' => $tagKVs[$tagName] ?? 0
];
}
$items[$ki]['tag_list'] = $tagList;
$createTime = is_numeric($item['create_time']) ? $item['create_time'] : strtotime($item['create_time']);
$items[$ki]['create_date'] = date('Y-m-d', $createTime);
$item['create_dateTime'] = date('Y-m-d H:i:s', $createTime);
}
} catch (\Exception $e) {
}
return $items;
}
public static function parseInfo($item)
{
if ($item) {
$tagStr = trim($item['tag'] ?? '');
$tagNameList = empty($tagStr) ? [] : explode(',', $tagStr);
$tagKVs = ArticleTags::findByNames($tagNameList)->column('id', 'name');
$tagList = [];
foreach ($tagNameList as $tagName) {
$tagList[] = [
'name' => $tagName,
'id' => $tagKVs[$tagName] ?? 0
];
}
$item['tag_list'] = $tagList;
$createTime = is_numeric($item['create_time']) ? $item['create_time'] : strtotime($item['create_time']);
$item['create_date'] = date('Y-m-d', $createTime);
$item['create_dateTime'] = date('Y-m-d H:i:s', $createTime);
}
return $item;
}
public static function countList(array $where, callable $call = null)
{
$q = new static();
$q = $q->where($where);
if ($call) {
$q = $call($q);
}
return $q->count();
}
public static function findListByWhere(array $where, int $page = 1, int $size = 10, callable $call = null, array $sortList = ['sort' => 'desc'])
{
$q = new static();
$q = $q->with(["archivesCategory"])->where($where);
if ($call) {
$q = $call($q);
}
if ($size > 0) {
$q = $q->page($page, $size);
}
if (count($sortList)) {
$q = $q->order($sortList);
}
return $q->select();
}
public static function getTeam()
{
return self::with(["archivesCategory"])
->where("category_id",Category::CATEGORY_TEAM)
->order(["sort"=>"desc","id"=>"desc"])
->select();
}
public static function getIndexTop($categoryId)
{
return self::with(["archivesCategory"])
->where("category_id",$categoryId)
->where("top",1)
->order(["sort"=>"desc"])
->find();
}
public static function getIndexList($categoryId,$num)
{
return self::with(["archivesCategory"])
->where("category_id",$categoryId)
->where("top","<>",1)
->order([ 'sort' => 'desc',"id"=>"desc"])
->limit($num)
->select();
}
}

84
app/model/ArticleTags.php Executable file
View File

@ -0,0 +1,84 @@
<?php
namespace app\model;
use think\Collection;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\Paginator;
/**
* 文章标签记录
* 当前只收录【客户案例】和【得助社区】的标签
*
* Class ArticleTags
* @package app\model
*/
class ArticleTags extends Base
{
/**
* @param string $name
* @return array|\think\Model|null
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public static function findByName(string $name)
{
return self::where('name', $name)->find();
}
/**
* @param array $nameList
* @return Collection
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public static function findByNames(array $nameList)
{
return self::whereIn('name', $nameList)->select();
}
// 获取推荐标签
public static function getRecommendTags(int $size = 12, int $type = -1)
{
$where = [];
$sortOrder = [];
switch ($type) {
case 1: // 限制查询 case_total
$where[] = ['case_total', '>', 0];
$sortOrder['case_total'] = 'desc';
break;
case 2: // 限制查询 news_total
$where[] = ['news_total', '>', 0];
$sortOrder['news_total'] = 'desc';
break;
}
$q = new static();
if (count($where) > 0) {
$q = $q->where($where);
}
if (count($sortOrder) > 0) {
$q = $q->order($sortOrder);
}
return $q->limit($size)->select();
}
/**
* 分页器:分页数据查询
*
* @param array $where 查询条件,二维数组
* @param array $paginateRows 分页参数 [list_rows 每页数量 | page 当前页 | path url路径 | query url额外参数| fragment url锚点 | var_page 分页变量]
* @param bool $paginateSimple 是否为简单分页
* @return Paginator
* @throws DbException
*/
public static function getListWithPaginate(array $where, array $paginateRows, bool $paginateSimple = false): Paginator
{
return self::where($where)->paginate($paginateRows, $paginateSimple);
}
}

54
app/model/AuthGroup.php Executable file
View File

@ -0,0 +1,54 @@
<?php
namespace app\model;
use think\facade\Cache;
class AuthGroup extends Base
{
public static function updateRules($groupId, $rules)
{
$rules = implode(',', $rules);
$data = ['rules' => $rules];
self::updateById($groupId, $data);
Member::where('group_id', $groupId)
->update($data);
}
//根据ID获取角色列表ID为1是超级管理员
public static function getListById($groupId = 1)
{
if($groupId < 1){
return [];
}
$group = self::getById($groupId);
if(empty($group)){
return [];
}
if($groupId == 1){
return self::select()
->toArray();
}else{
return self::where('id','<>','1')
->select()
->toArray();
}
}
/**
* 重置角色权限缓存
* @param int $groupId 指定重置的角色ID若不指定则重置所有角色
*/
public static function resetGroupRulesCache($groupId = 0)
{
if(is_numeric($groupId) && $groupId > 0) {
Cache::set('group_rules_'.$groupId, null);
Cache::set('rule_names_'.$groupId, null);
} else {
$groupIds = self::column('id');
foreach ($groupIds as $id){
Cache::set('group_rules_'.$id, null);
Cache::set('rule_names_'.$id, null);
}
}
}
}

271
app/model/AuthRule.php Executable file
View File

@ -0,0 +1,271 @@
<?php
namespace app\model;
class AuthRule extends Base
{
//根据权限IDs获取权限列表
public static function getListByRuleIDs($ruleIds)
{
$items = self::wherein('id',$ruleIds)
//->where('status', 1)
->order('sort', 'asc')
->select()
->toArray();
return self::getChildren($items);
}
//角色权限 + 基本权限
public static function getAuthListByRuleIDs($groupId)
{
$order = ['sort'=>'asc'];
if ($groupId == 1) {
$items = self::order($order)->select()->toArray();
} else {
$group = AuthGroup::getById($groupId);
$rulesArr = [];
if ($group && !empty($group['rules'])) {
$rulesArr = array_filter(explode(',', $group['rules']));
}
if (count($rulesArr) >0) {
$items = self::wherein('id', $rulesArr)->whereOr('is_base', 1)->order($order)->select()->toArray();
} else {
$items = self::where('is_base', 1)->order($order)->select()->toArray();
}
}
$levelItems = self::getChildren($items);
$levelIds = self::getChildrenIds($items);
// 独立写入不在连续分层中的权限,连续分层权限可用于菜单显示
foreach ($items as $item) {
if(!in_array($item['id'], $levelIds)) {
$levelItems[] = $item;
}
}
return $levelItems;
}
//根据上级ID获取下级权限列表
public static function getListTree($forMenu = 0)
{
$items = self::getList($forMenu);
return self::getChildren($items);
}
//根据上级ID获取下级权限列表
public static function getListByParentId($parentId)
{
return self::where('parent_id', $parentId)
->order('sort', 'asc')
->select()
->toArray();
}
/**
* @param array $items
* @param integer $parent_id
* @return array
*/
public static function getChildren($items, $parentId = 0)
{
$data = [];
foreach($items as $item){
if($item['parent_id'] == $parentId){
$childern = self::getChildren($items, $item['id']);
if(!empty($childern)){
$item['hasChildren'] = true;
$item['children'] = $childern;
}
$data[] = $item;
}
}
return $data;
}
public static function getChildrenIds($items, $parentId = 0)
{
$data = [];
foreach($items as $item){
if($item['parent_id'] == $parentId){
$childrenIds = self::getChildrenIds($items, $item['id']);
if(!empty($childrenIds)){
$data = array_merge($data, $childrenIds);
}
$data[] = $item['id'];
}
}
return $data;
}
/**
* @param integer $forMenu 大于0表示获取可显示的权限
* @return void
*/
private static function getList($forMenu = 0)
{
if($forMenu){
return self::where('status', 1)->order('sort', 'asc')->select()->toArray();
}else{
return self::order('sort', 'asc')->select()->toArray();
}
}
//获取可显示权限
public static function getTopListForDisplay()
{
return self::where('status', 1)
->where('parent_id', 0)
->order('sort', 'asc')
->select()
->toArray();
}
//根据两个名字获取权限
public static function getByTwoName($name, $otherName)
{
return self::whereOr('name',$name)
->whereOr('name',$otherName)
->findOrEmpty()
->toArray();
}
//根据name获取权限
public static function getByName($name)
{
return self::where('name', $name)
->findOrEmpty()
->toArray();
}
public static function onAfterInsert($rule)
{
$rule->sort = $rule->id;
$rule->save();
}
/**
* 该情况适合无限级菜单分组显示 当前系统仅支持2级权限因此推荐使用getListTree 方法)
* @param int $forMenu 是否只获取可显示菜单权限
*/
public static function getListTreeSort($forMenu = 0)
{
$items = self::getList($forMenu);
return self::groupSort($items, 0, 1, true, true);
}
/**
* 分组排序(不拆分为子集)
*
* @param $items 数据源
* @param integer $pid
* @param integer $level
* @param bool $clear 是否释放局部变量(外部调用时必须先释放,避免数据被累加;内部不用,需要累加)
* @param bool $isArr 传入的$items是否为数组默认否数据集
* @param string $levelSpare 分层符
* @return void
*/
public static function groupSort($items,$pid = 0, $level = 1, $clear = true, $isArr = false, $levelSpare = '_')
{
static $data = [];
if ($clear) {
$data = [];
static $data = [];
}
if(!empty($levelSpare) && $level > 1) {
$levelSpare = str_repeat($levelSpare, $level-1);
}
if (count($items) > 0) {
if ($isArr) {
foreach ($items as $key => $item) {
if ($item['parent_id'] == $pid) {
$item['level'] = $level;
$item['title'] = $levelSpare.$item['title'];
$data[] = $item;
self::groupSort($items, $item['id'], $level+1, false, $isArr);
}
}
} else {
foreach ($items as $key => $item) {
if ($item->parent_id == $pid) {
$item->level = $level;
$item->title = $levelSpare.$item->title;
$data[] = $item;
self::groupSort($items, $item->id, $level+1, false, $isArr);
}
}
}
}
return $data;
}
/**
* 树形排序 (拆分子集)
*
* @param Collection $items 数据集(非数组)
* @param integer $pid
* @param integer $level
* @return void
*/
public static function treeSort($items,$pid = 0, $level = 1)
{
$data = [];
if (count($items) > 0) {
foreach ($items as $key => $item) {
if ($item->parent_id == $pid) {
$item->level = $level;
$children = self::treeSort($items, $item->id, $level+1);
$item->children = $children;
$data[] = $item;
}
}
}
return $data;
}
/**
* 查询用户权限(登陆时更具角色更新为最新权限)
* 可显示权限和所有权限 角色权限 + 基本权限
*
* @param boolean $groupId 角色ID,超级管理员(对应group_id = 1)
* @return void
*/
public static function userRolesList($groupId = 1)
{
$userRoles = [];
$items = null;
$order = ['sort'=>'asc'];
if ($groupId == 1) {
$items = self::order($order)->select();
} else {
$group = AuthGroup::getById($groupId);
$rulesArr = [];
if ($group && !empty($group['rules'])) {
$rulesArr = array_filter(explode(',', $group['rules']));
}
if (count($rulesArr) >0) {
$items = self::wherein('id', $rulesArr)->whereOr('is_base', 1)->order($order)->select();
} else {
$items = self::where('is_base', 1)->order($order)->select();
}
}
if (empty($items) || $items->isEmpty()) {
return $userRoles;
}
$allRulesId = [];
$displayRules = [];
$displayRulesId = [];
foreach ($items as $key => $item) {
$allRulesId[] = $item->id;
if ($item->status > 0) {
$displayRules[] = $item;
$displayRulesId[] = $item->id;
}
}
$userRoles['allRules'] = $items;
$userRoles['allRulesId'] = $allRulesId;
$userRoles['displayRules'] = $displayRules;
$userRoles['displayRulesId'] = $displayRulesId;
return $userRoles;
}
}

51
app/model/Base.php Executable file
View File

@ -0,0 +1,51 @@
<?php
namespace app\model;
use think\Model;
class Base extends Model
{
protected $autoWriteTimestamp = false;
public const BOOL_TRUE = 1;
public const BOOL_FALSE = 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更新数据
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查询记录详情
public static function findInfoById($id)
{
return self::where('id', $id)->find();
}
}

170
app/model/Block.php Executable file
View File

@ -0,0 +1,170 @@
<?php
namespace app\model;
class Block extends Base
{
const BLOCK = 1;
const IMG = 2;
const TEXT = 3;
const GROUP = 4;
const VIDEO = 5;
const CODE = 6;
const AUDIOS = 7;
const VIDEOS = 8;
const MAP = 9;
// json化存储的区块类型
public static $jsonValueTypes = [self::GROUP,self::AUDIOS,self::MAP];
public static $mapType = [
"gaode"=>"高德",
"baidu"=>"百度",
"tengxun"=>"腾讯",
];
//获取类型键值对
public static function getTypes()
{
return [
'1' => 'block',
'2' => 'img',
'3' => 'text',
'4' => 'group',
'5' => 'video',
'6' => 'code',
'7' => 'audios',
'8' => 'videos',
'9' => 'map',
];
}
//根据键值和类目获取数据
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 [];
}
$category = Category::getById($categoryId);
if(empty($category)){
return [];
}
$items = self::where('category_id', $categoryId)
->order('sort asc')
->select()
->toArray();
if(empty($items)){
return [];
}
$data = [];
foreach($items as $item){
$data[$item['keyword']] = $item;
}
return $data;
}
//根据栏目ID获取块列表
public static function getChildrenByCategoryId($categoryId)
{
if($categoryId <= 0){
return [];
}
$category = Category::getById($categoryId);
if(empty($category)){
return [];
}
$categoryIds = Category::where('path', 'like', $category['path'].$category['id'].',%')->column('id');
if(empty($categoryIds)){
return [];
}
$items = self::whereIn('category_id', $categoryIds)
->order('sort asc')
->select()
->toArray();
if(empty($items)){
return [];
}
$data = [];
foreach($items as $item){
$data[$item['keyword']] = $item;
}
return $data;
}
public static function onAfterInsert($item)
{
$item->sort = $item->id;
$item->save();
}
//获取在使用中的文件
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;
}
// 转化json化存储的数据
public static function convertValue($items)
{
foreach ($items as &$item) {
$item['json_value_list'] = [];
if(in_array($item['type'], self::$jsonValueTypes) && !empty($item['value'])) {
$item['json_value_list'] = json_decode($item['value'], true);
}
}
unset($item);
return $items;
}
}

471
app/model/Category.php Executable file
View File

@ -0,0 +1,471 @@
<?php
namespace app\model;
use think\Collection;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
class Category extends Base
{
// 文章类
// 新闻动态
public const CATEGORY_NEWS = 7;
//团队管理
public const CATEGORY_TEAM = 6;
//企业介绍
public const CATEGORY_COMPANY = 2;
//物业服务
public const CATEGORY_SERVICE = 8;
//新闻碎片
public const CATEGORY_NEWS_BLOCK = 14;
//新闻碎片
public const CATEGORY_JOIN_BLOCK = 15;
//新闻碎片
public const CATEGORY_SERVE_BLOCK = 16;
// 案例模版
public const TEMPLATE_NEWS = 'news_list';
// 案例模版
public const TEMPLATE_CASES = 'cases_list';
// 解决方案模版
public const TEMPLATE_SCHEME = 'scheme_list';
// 路由
public const RULE_PRODUCT = 'product';
public const RULE_DOWNLOAD = 'download';
// 内容以文本域形式录入配置,默认内容为富文本形式录入
public static $contentRawCategoryList = [
];
// 允许设置标签的栏目
public static $allowTagCategoryList = [
];
/*
* ====================================================
* 分割线
* ====================================================
*/
//获取首页栏目ID
public static function getIndex()
{
return self::where('is_index', 1)->findOrEmpty()->toArray();
}
/**
* 根据上级ID和语言获取下级栏目
*
* @param $parentId
* @param bool $onlyChild 仅获取下级 默认true false=获取所有后代
* @return array
*/
public static function getChildrenByParentId($parentId, bool $onlyChild = true)
{
if ($parentId <= 0) {
return [];
}
$category = self::getById($parentId);
if (empty($category)) {
return [];
}
if ($onlyChild) {
$where[] = ['c.parent_id', '=', $parentId];
} else {
$where[] = ['c.path', 'like', $category['path'].$parentId.',%'];
}
return self::alias('c')
->leftJoin('model m', 'c.model_id=m.id')
->field('c.*, m.manager, m.template, m.name as modelName')
// ->where('c.parent_id', $parentId)
->where($where)
->order('c.sort', 'asc')
->order('c.id', 'asc')
->select()
->toArray();
}
//重写方法
public static function getById($categoryId)
{
return self::alias('c')
->leftJoin('model m', 'c.model_id = m.id')
->where('c.id', $categoryId)
->field('c.*, m.template')
->findOrEmpty()
->toArray();
}
//查看是否有下级栏目
public static function hasChildren($categoryId)
{
if (is_array($categoryId)) {
$count = self::where('parent_id', 'in', $categoryId)->count();
} else {
$count = self::where(['parent_id' => $categoryId])->count();
}
return $count ? true : false;
}
//获取前台菜单
public static function getListForFrontMenu()
{
$items = self::alias('c')
->leftJoin('model m', 'c.model_id=m.id')
->field('c.*, m.manager, m.template')
->where('c.state', 1)
->where('c.frontend_show', 1)
->order('is_index desc, sort asc')
->select()
->toArray();
return self::getCates($items);
}
/**
* 获取栏目
* @param bool $limit 是否限制查询
* @param array $cates 需要限制查询的栏目IDs
*/
public static function getList($limit = false, $cates = [])
{
if ($limit && empty($cates)) {
return [];
}
return self::alias('c')
->leftJoin('model m', 'c.model_id=m.id')
->field('c.*, m.manager, m.name as modelName')
->when($limit, function ($query) use ($cates) {
$query->whereIn('c.id', $cates);
})
->order('sort', 'asc')
->select()
->toArray();
}
//获取栏目分级列表
public static function getListTree($isMenu = false)
{
if ($isMenu) {
$items = self::where('c.state', 1)->order('sort', 'asc')->select()->toArray();
} else {
$items = self::order('sort', 'asc')->select()->toArray();
}
return self::getCates($items);
}
//获取递归栏目
public static function getCates($cates, $parentId = 0)
{
$menus = [];
foreach ($cates as $cate) {
if ($cate['parent_id'] == $parentId) {
$children = self::getCates($cates, $cate['id']);
if (!empty($children)) {
$cate['children'] = $children;
}
$menus[] = $cate;
}
}
return $menus;
}
public static function onAfterInsert($category)
{
$category->sort = $category->id;
$category->save();
}
//递归获取栏目名称面包屑
public static function getCatesCrumbs($currentId = 0)
{
$crumbs = [];
$category = self::getById($currentId);
if ($category) {
if ($category['parent_id'] != 0) {
$categoryIds = explode(',', trim($category['path'], ','));
$categories = self::where('id', 'in', $categoryIds)->column('*', 'id');
foreach ($categoryIds as $id) {
if (isset($categories[$id])) {
$crumbs[] = $categories[$id]['title'];
}
}
}
$crumbs[] = $category['title'];
}
return $crumbs;
}
//获取栏目中涉及到的文件
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;
}
//当前分类的最高级分类Id
public static function firstGradeById($id)
{
$res = 0;
$item = self::getById($id);
if ($item) {
$res = $id;
if ($item['parent_id'] > 0) {
$items = self::select()->toArray();
$first = self::getFirstGrade($items, $item['parent_id']);
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['parent_id'] > 0) {
unset($items[$key]);
$parent = self::getFirstGrade($items, $item['parent_id']);
if (!empty($parent)) {
$data = $parent;
} else {
$data = $item;
}
} else {
$data = $item;
}
}
}
return $data;
}
/**
* 当前分类和子分类
*
* @param int $parentId
* @return array
*/
public static function getCategoryWithChildren(int $parentId)
{
$item = self::getById($parentId);
if ($item) {
$childList = self::getChildrenByParentId($parentId);
$childIds = [];
foreach ($childList as $child) {
$childIds[] = $child['id'];
}
$item['children'] = $childList;
$item['children_ids'] = $childIds;
}
return $item;
}
/**
* 当前分类和子分类的ID集
* @param int $parentId
* @return array|int[]
*/
public static function getCategoryWithChildrenIds(int $parentId)
{
$categoryIds = [$parentId];
$item = self::getCategoryWithChildren($parentId);
if ($item) {
$categoryIds = array_merge($categoryIds, $item['children_ids']);
}
return $categoryIds;
}
public static function getByRuleAlias($alias)
{
return self::where('route', $alias)->findOrEmpty()->toArray();
}
/*
* 设置了路由别名的栏目
* 路由别名长度大的优先排序,路由匹配优先规则
*/
public static function getRouteList()
{
return self::whereRaw('char_length(route) > 0')
->field('id,route,model_id, char_length(route) as alias_len')
->order('alias_len', 'desc')
->select()
->toArray();
}
/**
* 获取所有下级分类
*
* @param int $parentId
* @return Collection
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public static function findAllChildren(int $parentId)
{
return self::whereFindInSet('path', $parentId)->select();
}
/**
* 根据栏目分组ID获取该分组所有的栏目ID集
*
* @param int $groupId
* @return array|int[]
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public static function getGroupCategoryIds(int $groupId)
{
$list = [$groupId];
$items = self::findAllChildren($groupId);
$childrenIds = $items->column('id');
return array_merge($list, $childrenIds);
}
/**
* 判断当前分类是否为文章类
*
* @param int $cateId
*/
public static function isArticle(int $cateId)
{
$item = self::getById($cateId);
$res = false;
if (!empty($item) && $item['model_id'] == Model::MODEL_ARTICLE) {
$res = true;
}
return $res;
}
/**
* 根据栏目标识获取栏目信息
*
* @param string $categoryKey
* @return array|\think\Model|null
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public static function findByCategoryKey(string $categoryKey)
{
if (strlen($categoryKey) == 0) {
return null;
}
return self::where('category_key', $categoryKey)->find();
}
public static function navList($categoryId = 0)
{
$menus = self::getListForFrontMenu();
$allCategories = Category::getList(false);
foreach ($menus as &$menu) {
$menu['children_ids'] = [];
// $menusGroup = [];
if (isset($menu['children'])) {
$menu['children_ids'] = array_column($menu['children'], 'id');
// foreach ($menu['children'] as $child) {
// $menusGroup[$child['group_name']][] = $child;
// }
}
// $menu['menus_group'] = $menusGroup;
$menu['all_subordinate_ids'] = self::getAllSubordinateIds($allCategories, $menu['id'], true);
$menu['isActive'] = 0;
if ($categoryId == $menu['id'] || in_array($categoryId, $menu['all_subordinate_ids'])) {
$menu['isActive'] = 1;
}
}
$data = [
// 'categoryId' => $categoryId,
'menus' => $menus,
];
// var_dump($menus);exit;
return $data;
}
// 所有下级栏目ID 包含当前栏目ID
private static function getAllSubordinateIds($items, $curId = 0, $containCurrent = false)
{
$list = [];
foreach ($items as $k => $item) {
if ($item['parent_id'] == $curId) {
$list[] = $item['id'];
unset($items[$k]);
$childrenIds = self::getAllSubordinateIds($items, $item['id']);
if ($childrenIds) {
$list = array_merge($list, $childrenIds);
}
}
}
if ($containCurrent) {
$list = array_merge([$curId], $list);
}
return $list;
}
//当前分类的最高级分类Id
public static function getPosition($id)
{
$position = "";
$item = self::getById($id);
if ($item) {
$position= "<a href=\"".getUri($item)."\">{$item['title']}</a>";
if ($item['parent_id'] > 0) {
$items = self::select()->toArray();
$first = self::getPositionGrade($items, $item['parent_id']);
if (!empty($first)) {
$position = $first.$position;
}
}
}
return $position;
}
public static function getPositionGrade($items, $parentId)
{
$data="";
foreach ($items as $key => $item) {
if ($item['id'] == $parentId) {
$data = "<a href=\"".getUri($item)."\">{$item['title']}</a>";
if ($item['parent_id'] > 0) {
unset($items[$key]);
$parent = self::getFirstGrade($items, $item['parent_id']);
if (!empty($parent)) {
$data = "<a href=\"".getUri($parent)."\">{$parent['title']}</a>".$data;
}
}
}
}
return $data;
}
}

115
app/model/ConfigSetting.php Executable file
View File

@ -0,0 +1,115 @@
<?php
namespace app\model;
use think\facade\Config as CConfig;
class ConfigSetting extends Base
{
// 是否开启同步生成扩展配置文件
const with_write_config = true;
public static function setConfigByName(string $name, array $content)
{
$config = self::getConfigByName($name);
if(empty($config)) {
self::create([
'name' => $name,
'contents' => json_encode($content, JSON_UNESCAPED_UNICODE)
]);
} else {
self::updateById($config['id'], [
'contents' => json_encode($content, JSON_UNESCAPED_UNICODE)
]);
}
// 同步写入与生成配置文件
if (self::with_write_config) {
self::setWithExtraConfigFile($name, $content);
}
}
public static function getConfigByName(string $name)
{
$item = self::where('name', $name)
->findOrEmpty()
->toArray();
return self::convertContents($item);
}
// 通过数据库查询配置信息
public static function getConfigContentsByName(string $name)
{
$item = self::getConfigByName($name);
return $item['contents'] ?? [];
}
public static function convertContents(array $item)
{
if(empty($item)) {
return [];
}
$item['contents'] = empty($item['contents']) ? [] : json_decode($item['contents'], true);
return $item;
}
// 优先通过扩展配置文件查询配置信息,没有查询到再从数据库中查询
public static function getConfigContentsWithFileByName(string $name)
{
$conf = [];
if (self::with_write_config) {
$extraFileName = self::parseExtraConfigFileNameByName($name);
CConfig::load('extra/'.$extraFileName, $name);
$conf = config($name);
}
if (!empty($conf)) {
return $conf;
} else {
$item = self::getConfigByName($name);
return $item['contents'] ?? [];
}
}
// 通过配置名称解析扩展配置文件名称
public static function parseExtraConfigFileNameByName(string $name)
{
// extra开头
if (substr($name, 0, 5) == 'extra') {
$configFileName = substr($name, 5);
// 字符串首字母转小写
$configFileName = lcfirst($configFileName);
} else {
$configFileName = $name;
}
return $configFileName;
}
// 扩展配置信息写入扩展配置文件
public static function setWithExtraConfigFile(string $name, array $content = [])
{
try {
$configFileName = self::parseExtraConfigFileNameByName($name);
if (!is_numeric($configFileName) && empty($configFileName)) {
return false;
}
$extraPath = config_path().'extra/';
checkPathExistWithMake($extraPath);
$configFil = $extraPath.$configFileName.'.php';
$configData = var_export($content, true);
file_put_contents($configFil, '<?php' . PHP_EOL . 'return ' . $configData . ';');
return true;
} catch (\Exception $e) {
\think\facade\Log::write('请检查扩展配置文件目录config/extra是否拥有可读写权限', 'error');
return false;
}
}
}

96
app/model/DownloadModel.php Executable file
View File

@ -0,0 +1,96 @@
<?php
namespace app\model;
use think\Paginator;
class DownloadModel extends Base
{
protected $name = 'download';
public static function onAfterInsert($item)
{
$item->sort = $item->id;
$item->save();
}
public static function getPaginateList($categoryId, $per = 20, $isSample = false)
{
$paginate = [
'list_rows' => $per,
'query' => ['category_id' => $categoryId]
];
return self::where('category_id', $categoryId)->order('sort', 'desc')->paginate($paginate, $isSample);
}
public static function getByCategoryId($categoryId, $onlyVisible = false, $pre = 50)
{
$items = self::where('category_id', $categoryId)
->when($onlyVisible, function($query){
$query->where('visible', 1);
})
->order('sort', 'desc')
->limit($pre)
->select();
return $items->toArray();
}
/**
* 获取列表
* @param int $categoryId 分类ID
* @param int $per 每页数量
* @param string $keyword 关键词
* @param array $param 类型:置顶、热门、推荐 ['top','hot','recommend']
* @param int $status 状态,-1表示不限制
* @param array $orderList 排序
* @param bool $onlyChild 仅获取下级 默认true false=获取所有后代分类
* @return Paginator
*/
public static function getList(int $categoryId, int $per = 20, string $keyword = '', array $param = [], int $status = -1, array $orderList = ['a.sort' => 'desc'], bool $onlyChild = true)
{
$whereMap = [];
$pageParam = [];
if (is_numeric($categoryId) && $categoryId > 0) {
$children = Category::getChildrenByParentId($categoryId, $onlyChild);
if (!empty($children)) {
$categoryIds = [$categoryId];
foreach ($children as $child) {
if ($child['model_id'] == Model::MODEL_DOWNLOAD) {
$categoryIds[] = $child['id'];
}
}
$whereMap[] = ['a.category_id', 'in', $categoryIds];
} else {
$whereMap[] = ['a.category_id', '=', $categoryId];
}
$pageParam['category_id'] = $categoryId;
}
if (!empty($keyword)) {
$whereMap[] = ['a.title', 'like', '%'.$keyword.'%'];
$pageParam['keyword'] = $keyword;
}
if (is_array($param) && count($param) > 0) {
$pageParam['param'] = $param;
foreach ($param as $vo) {
if (in_array($vo, ['top', 'hot', 'recommend'], true)) {
$whereMap[] = ["a.{$vo}", '=', 1];
}
}
}
$paginate = [
'list_rows' => $per,
'query' => $pageParam
];
return self::alias('a')
->leftJoin('category c', 'c.id = a.category_id')
->when(count($whereMap) > 0, function ($query) use ($whereMap) {
$query->where($whereMap);
})
->when($status != -1, function ($query) use ($status) {
$query->where('a.visible', $status);
})
->order($orderList)
->field('a.*, c.title as category_title')
->paginate($paginate, false);
}
}

96
app/model/File.php Executable file
View File

@ -0,0 +1,96 @@
<?php
namespace app\model;
use think\Image;
class File extends Base
{
const IMG = 'img';
const VIDEO = 'video';
const FILE = 'file';
const AUDIO = 'audio';
//获取文件类型
public static function getTypes()
{
return [
'img' => '图片',
'video' => '视频',
'file' => '文件',
'audio' => '音频',
];
}
//获取文件列表
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, $type = 'img')
{
$realPath = app()->getRootPath() . ltrim($src,'/');
if(is_file($realPath) && $type == 'img'){
$img = Image::open($realPath);
list($w,$h) = $img->size();
$w_h = $w . 'px * ' . $h . 'px';
}else{
$w_h = '';
}
return self::create([
'type' => $type,
'name' => $file->getOriginalName(),
'src' => $src,
'size' => $file->getSize(),
'suffix' => $file->getOriginalExtension(),
'mime_type' => $file->getOriginalMime(),
'create_time' => time(),
'w_h' => $w_h,
'md5' => $file->md5()
]);
}
//获取所有记录
public static function getAll()
{
return self::select()->toArray();
}
}

47
app/model/History.php Executable file
View File

@ -0,0 +1,47 @@
<?php
namespace app\model;
class History extends Base
{
public static function onAfterInsert($item)
{
$item->sort = $item->id;
$item->save();
}
public static function getPaginateList($categoryId, $per = 20, $isSample = false)
{
$paginate = [
'list_rows' => $per,
'query' => ['category_id' => $categoryId]
];
$items = self::where('category_id', $categoryId)->order('sort', 'asc')->paginate($paginate, $isSample);
if(!$items->isEmpty()) {
$ids = $items->column('id');
$infoList = HistoryInfo::getByHistoryIds($ids);
foreach ($items as $k => $item) {
$items[$k]['info'] = $infoList[$item['id']] ?? [];
}
}
return $items;
}
public static function getByCategoryId($categoryId, $onlyVisible = false, $pre = 50)
{
$items = self::where('category_id', $categoryId)
->when($onlyVisible, function($query){
$query->where('visible', 1);
})
->order('sort', 'asc')
->limit($pre)
->select();
if(!$items->isEmpty()) {
$ids = $items->column('id');
$infoList = HistoryInfo::getByHistoryIds($ids, $onlyVisible);
foreach ($items as $k => $item) {
$items[$k]['info'] = $infoList[$item['id']] ?? [];
}
}
return $items->toArray();
}
}

52
app/model/HistoryInfo.php Executable file
View File

@ -0,0 +1,52 @@
<?php
namespace app\model;
class HistoryInfo extends Base
{
public static function onAfterInsert($item)
{
$item->sort = $item->id;
$item->save();
}
public static function getByHistoryIds($historyIds = [], $onlyVisible = false)
{
if(!is_array($historyIds) || count($historyIds) == 0) {
return [];
}
$list = self::whereIn('history_id', $historyIds)
->when($onlyVisible, function ($query) {
$query->where('visible', 1);
})
->order(['history_id'=>'asc','sort'=>'asc'])
->select()
->toArray();
$data = [];
foreach ($list as $item) {
$item['img_list'] = [];
if(!empty($item['imgs'])) {
$item['img_list'] = array_filter(explode(',', $item['imgs']));
}
$data[$item['history_id']][] = $item;
}
return $data;
}
public static function countByHistoryId($historyId)
{
return self::where('history_id', $historyId)->count();
}
public static function delByHistoryId($historyId)
{
return self::where('history_id', $historyId)->delete();
}
public static function getByHistoryId($historyId)
{
if($historyId <= 0) {
return [];
}
return self::where('history_id', $historyId)->order(['sort'=>'asc'])->select()->toArray();
}
}

32
app/model/Honour.php Executable file
View File

@ -0,0 +1,32 @@
<?php
namespace app\model;
class Honour extends Base
{
public static function onAfterInsert($item)
{
$item->sort = $item->id;
$item->save();
}
public static function getPaginateList($categoryId, $per = 20, $isSample = false)
{
$paginate = [
'list_rows' => $per,
'query' => ['category_id' => $categoryId]
];
return self::where('category_id', $categoryId)->order('sort', 'asc')->paginate($paginate, $isSample);
}
public static function getByCategoryId($categoryId, $onlyVisible = false, $pre = 50)
{
$items = self::where('category_id', $categoryId)
->when($onlyVisible, function($query){
$query->where('visible', 1);
})
->order('sort', 'asc')
->limit($pre)
->select();
return $items->toArray();
}
}

52
app/model/Link.php Executable file
View File

@ -0,0 +1,52 @@
<?php
namespace app\model;
class Link extends Base
{
public static function delByIds($ids)
{
return self::whereIn('id', $ids)->delete();
}
//获取友情链接
public static function getList($limit=0)
{
return self::order('sort asc')
->when($limit > 0, function($q) use ($limit) {
$q->limit($limit);
})
->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;
}
// 分页查询
public static function getListWithPaginate($where=[], $limit=10, $simplePage = false)
{
return self::where($where)
->order('sort asc')
->paginate([
'list_rows' => $limit
], $simplePage);
}
}

51
app/model/LinkProduct.php Executable file
View File

@ -0,0 +1,51 @@
<?php
namespace app\model;
/**
* 产品快链管理
*
* Class LinkProduct
* @package app\model
*/
class LinkProduct extends Base
{
public static function delByIds($ids)
{
return self::whereIn('id', $ids)->delete();
}
//获取列表
public static function getList($limit = 10, $where = [], $orders = ['sort'=>'asc'])
{
return self::order($orders)
->when(!empty($where), function ($q) use ($where) {
$q->where($where);
})
->when($limit > 0, function ($q) use ($limit) {
$q->limit($limit);
})
->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;
}
}

20
app/model/Log.php Executable file
View File

@ -0,0 +1,20 @@
<?php
namespace app\model;
class Log extends Base
{
//记录操作日志
public static function write($controller, $action, $content)
{
$auth = session('auth');
return self::create([
'member_id' => $auth['userId'],
'name' => $auth['userName'],
'ip' => request()->ip(),
'create_time' => time(),
'controller' => $controller,
'action' => $action,
'content' => $content
]);
}
}

6
app/model/LoginLog.php Executable file
View File

@ -0,0 +1,6 @@
<?php
namespace app\model;
class LoginLog extends Base
{
}

72
app/model/Member.php Executable file
View File

@ -0,0 +1,72 @@
<?php
namespace app\model;
class Member extends Base
{
public static function getList($limit = 40)
{
$items = 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);
return $items;
}
/**
* 根据角色分组返回用户
* @param int $groupId 角色分组ID
* @param int $limit 每页数量
*/
public static function getListByGroup($groupId, $limit = 40)
{
$items = 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);
return $items;
}
/**
* 根据角色分组返回用户
* @param int $limit 每页数量
* @return mixed
*/
public static function getListNotAdmin(int $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', '<>', 1)
->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);
}
}

60
app/model/Message.php Executable file
View File

@ -0,0 +1,60 @@
<?php
namespace app\model;
class Message extends Base
{
/**
* 获取留言列表
*/
public static function getList($per = 20, $startDate='', $endDate='')
{
$param = [];
if(!empty($startDate)) {
$param['startDate'] = $startDate;
}
if(!empty($endDate)) {
$param['endDate'] = $endDate;
}
$paginate = [
'list_rows' => $per,
'query' => $param
];
$items = self::when(!empty($startDate) && strtotime($startDate), function ($query) use($startDate) {
$startTime = strtotime(date('Y-m-d 00:00:00', strtotime($startDate)));
$query->where('create_time', '>=', $startTime);
})
->when(!empty($endDate) && strtotime($endDate), function ($query) use($endDate) {
$endTime = strtotime(date('Y-m-d 23:59:59', strtotime($endDate)));
$query->where('create_time', '<=', $endTime);
})
->order("create_time", 'desc')
->paginate($paginate);
return $items;
}
public static function onAfterInsert($item)
{
$item->create_time = time();
$item->save();
}
public static function getExportList($startDate='', $endDate='', $limit = 10000)
{
return self::when(!empty($startDate) && strtotime($startDate), function ($query) use($startDate) {
$startTime = strtotime(date('Y-m-d 00:00:00', strtotime($startDate)));
$query->where('create_time', '>=', $startTime);
})
->when(!empty($endDate) && strtotime($endDate), function ($query) use($endDate) {
$endTime = strtotime(date('Y-m-d 23:59:59', strtotime($endDate)));
$query->where('create_time', '<=', $endTime);
})
->order('create_time', 'desc')
->limit($limit)
->select()
->toArray();
}
}

27
app/model/Model.php Executable file
View File

@ -0,0 +1,27 @@
<?php
namespace app\model;
class Model extends Base
{
// 文章模型ID
const MODEL_ARTICLE = 31;
// 单页模型
const MODEL_PAGE = 33;
// 产品模型
const MODEL_PRODUCT = 36;
// 下载中心
const MODEL_DOWNLOAD = 37;
//获取模型列表
public static function getList()
{
return self::order('sort asc')
->select()
->toArray();
}
public static function onAfterInsert($model)
{
$model->sort = $model->id;
$model->save();
}
}

96
app/model/PositionModel.php Executable file
View File

@ -0,0 +1,96 @@
<?php
namespace app\model;
use think\Paginator;
class PositionModel extends Base
{
protected $name = 'position';
public static function onAfterInsert($item)
{
$item->sort = $item->id;
$item->save();
}
public static function getPaginateList($categoryId, $per = 20, $isSample = false)
{
$paginate = [
'list_rows' => $per,
'query' => ['category_id' => $categoryId]
];
return self::where('category_id', $categoryId)->order('sort', 'desc')->paginate($paginate, $isSample);
}
public static function getByCategoryId($categoryId, $onlyVisible = false, $pre = 50)
{
$items = self::where('category_id', $categoryId)
->when($onlyVisible, function($query){
$query->where('visible', 1);
})
->order('sort', 'asc')
->limit($pre)
->select();
return $items->toArray();
}
/**
* 获取列表
* @param int $categoryId 分类ID
* @param int $per 每页数量
* @param string $keyword 关键词
* @param array $param 类型:置顶、热门、推荐 ['top','hot','recommend']
* @param int $status 状态,-1表示不限制
* @param array $orderList 排序
* @param bool $onlyChild 仅获取下级 默认true false=获取所有后代分类
* @return Paginator
*/
public static function getList(int $categoryId, int $per = 20, string $keyword = '', array $param = [], int $status = -1, array $orderList = ['a.sort' => 'desc'], bool $onlyChild = true)
{
$whereMap = [];
$pageParam = [];
if (is_numeric($categoryId) && $categoryId > 0) {
$children = Category::getChildrenByParentId($categoryId, $onlyChild);
if (!empty($children)) {
$categoryIds = [$categoryId];
foreach ($children as $child) {
if ($child['model_id'] == Model::MODEL_PRODUCT) {
$categoryIds[] = $child['id'];
}
}
$whereMap[] = ['a.category_id', 'in', $categoryIds];
} else {
$whereMap[] = ['a.category_id', '=', $categoryId];
}
$pageParam['category_id'] = $categoryId;
}
if (!empty($keyword)) {
$whereMap[] = ['a.title', 'like', '%'.$keyword.'%'];
$pageParam['keyword'] = $keyword;
}
if (is_array($param) && count($param) > 0) {
$pageParam['param'] = $param;
foreach ($param as $vo) {
if (in_array($vo, ['top', 'hot', 'recommend'], true)) {
$whereMap[] = ["a.{$vo}", '=', 1];
}
}
}
$paginate = [
'list_rows' => $per,
'query' => $pageParam
];
return self::alias('a')
->leftJoin('category c', 'c.id = a.category_id')
->when(count($whereMap) > 0, function ($query) use ($whereMap) {
$query->where($whereMap);
})
->when($status != -1, function ($query) use ($status) {
$query->where('a.visible', $status);
})
->order($orderList)
->field('a.*, c.title as category_title')
->paginate($paginate, false);
}
}

94
app/model/ProductModel.php Executable file
View File

@ -0,0 +1,94 @@
<?php
namespace app\model;
use think\Paginator;
class ProductModel extends Base
{
protected $name = 'product';
public static function onAfterInsert($item)
{
$item->sort = $item->id;
$item->save();
}
public static function getPaginateList($categoryId, $per = 20, $isSample = false)
{
$paginate = [
'list_rows' => $per,
'query' => ['category_id' => $categoryId]
];
return self::where('category_id', $categoryId)->order('sort', 'desc')->paginate($paginate, $isSample);
}
public static function getByCategoryId($categoryId, $onlyVisible = false, $pre = 50)
{
$items = self::where('category_id', $categoryId)
->when($onlyVisible, function($query){
$query->where('visible', 1);
})
->order('sort', 'asc')
->limit($pre)
->select();
return $items->toArray();
}
/**
* 获取列表
* @param int $categoryId 分类ID
* @param int $per 每页数量
* @param string $keyword 关键词
* @param array $param 类型:置顶、热门、推荐 ['top','hot','recommend']
* @param int $status 状态,-1表示不限制
* @param array $orderList 排序
* @param bool $onlyChild 仅获取下级 默认true false=获取所有后代分类
* @return Paginator
*/
public static function getList(int $categoryId, int $per = 20, string $keyword = '', array $param = [], int $status = -1, array $orderList = ['a.sort' => 'desc'], bool $onlyChild = true, array $queryParam = [])
{
$whereMap = [];
if (is_numeric($categoryId) && $categoryId > 0) {
$children = Category::getChildrenByParentId($categoryId, $onlyChild);
if (!empty($children)) {
$categoryIds = [$categoryId];
foreach ($children as $child) {
if ($child['model_id'] == Model::MODEL_PRODUCT) {
$categoryIds[] = $child['id'];
}
}
$whereMap[] = ['a.category_id', 'in', $categoryIds];
} else {
$whereMap[] = ['a.category_id', '=', $categoryId];
}
}
if (!empty($keyword)) {
$whereMap[] = ['a.title', 'like', '%'.$keyword.'%'];
$pageParam['keyword'] = $keyword;
}
if (is_array($param) && count($param) > 0) {
$pageParam['param'] = $param;
foreach ($param as $vo) {
if (in_array($vo, ['top', 'hot', 'recommend'], true)) {
$whereMap[] = ["a.{$vo}", '=', 1];
}
}
}
$paginate = [
'list_rows' => $per,
'query' => $queryParam
];
return self::alias('a')
->leftJoin('category c', 'c.id = a.category_id')
->when(count($whereMap) > 0, function ($query) use ($whereMap) {
$query->where($whereMap);
})
->when($status != -1, function ($query) use ($status) {
$query->where('a.visible', $status);
})
->order($orderList)
->field('a.*, c.title as category_title')
->paginate($paginate, false);
}
}

43
app/model/Slide.php Executable file
View File

@ -0,0 +1,43 @@
<?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;
}
$mobileSrc = trim($item['src_mobile']);
if(!empty($mobileSrc)){
$key = getKeyByPath($mobileSrc);
$data[$key] = $mobileSrc;
}
}
return $data;
}
}

30
app/model/SmsLog.php Executable file
View File

@ -0,0 +1,30 @@
<?php
namespace app\model;
/**
* 短信记录
* Class SmsLog
* @package app\model
*/
class SmsLog extends Base
{
public static $typeList = [
'resister' => 'reg',
'appointment' => 'appoint',
];
public static function countByPhone($phone, $starTime=0,$endTime=0, $type='')
{
return self::where('phone', $phone)
->when($starTime > 0, function ($query) use($starTime) {
$query->where('create_time', '>=', $starTime);
})
->when($endTime > 0, function ($query) use($endTime) {
$query->where('create_time', '<=', $endTime);
})
->when(!empty($type) && in_array($type, self::$typeList), function ($query) use($type) {
$query->where('type', $type);
})
->count();
}
}

23
app/model/SpecialRoute.php Executable file
View File

@ -0,0 +1,23 @@
<?php
namespace app\model;
//特殊路由表
class SpecialRoute extends Base
{
const type_archives = "archives";
const type_archives_category = "archives_category";
public static function findOneByUrl(string $url="")
{
return self::where('route', $url)->find();
}
public static function deleteByTypeIds($ids,$type)
{
return self::where([['relation_id', "in",$ids],["type","=",$type]])->delete();
}
public static function findByTypeRelaTioneId($relationId,$type)
{
return self::where([['relation_id', "=",$relationId],["type","=",$type]])->find();
}
}

59
app/model/System.php Executable file
View File

@ -0,0 +1,59 @@
<?php
namespace app\model;
use think\facade\Config as CConfig;
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()
{
$system = self::order('id asc')
->findOrEmpty()
->toArray();
CConfig::load('extra/base', "extraBase");
$extraBase = config("extraBase");
return array_merge($extraBase,$system);
}
}

11
app/model/VisitLogoModel.php Executable file
View File

@ -0,0 +1,11 @@
<?php
namespace app\model;
use think\Paginator;
class VisitLogoModel extends Base
{
protected $name = 'visit_log';
}

5
app/provider.php Executable file
View File

@ -0,0 +1,5 @@
<?php
// 容器Provider定义文件
return [
'think\Paginator' => 'dxtc\Bootstrap'
];

188
app/service/File.php Executable file
View File

@ -0,0 +1,188 @@
<?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 File
{
//上传文件移动到上传文件夹
public static function move(UploadedFile $file)
{
$upload_path = '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];
}
//导出
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;
}
}

168
app/service/Http.php Executable file
View File

@ -0,0 +1,168 @@
<?php
namespace app\service;
class Http
{
public static function curlGet($url,$apiFields = null, $header = [])
{
$ch = curl_init();
if(!empty($apiFields)){
$i = 1;
foreach ($apiFields as $key => $value){
if($i == 1) {
if(mb_stripos($url, '?') !== FALSE) {
$url .= "&" ."$key=" . urlencode($value);
} else {
$url .= "?" ."$key=" . urlencode($value);
}
} else {
$url .= "&" ."$key=" . urlencode($value);
}
$i ++;
}
}
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FAILONERROR, false);
if(empty($header)){
curl_setopt($ch, CURLOPT_HEADER, false);
}else{
curl_setopt($ch,CURLOPT_HTTPHEADER, $header);
}
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
//https ignore ssl check ?
if(strlen($url) > 5 && strtolower(substr($url,0,5)) == "https" ){
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
}
$reponse = curl_exec($ch);
if (curl_errno($ch)){
throw new \Exception(curl_error($ch),0);
}else{
$httpStatusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (200 !== $httpStatusCode){
throw new \Exception($reponse,$httpStatusCode);
}
}
curl_close($ch);
return $reponse;
}
public static function curlPost($url, $postFields = null, $header = [], $hasFile = false)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_FAILONERROR, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
//https 请求
if(strlen($url) > 5 && strtolower(substr($url,0,5)) == "https" ) {
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
}
curl_setopt($ch, CURLOPT_POST, true);
if (is_array($postFields) && 0 < count($postFields))
{
if($hasFile) {
// CURLOPT_POSTFIELDS 值为数组则以multipart/form-data形式提交
curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields);
} else {
$jsonHeader = array("Content-Type: application/json; charset=utf-8", "Content-Length:".strlen(json_encode($postFields)));
$header = array_merge($header, $jsonHeader);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($postFields));
}
if(!empty($header)) {
curl_setopt($ch,CURLOPT_HTTPHEADER, $header);
}
}
$reponse = curl_exec($ch);
if (curl_errno($ch))
{
throw new \Exception(curl_error($ch),0);
}
else
{
$httpStatusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (200 !== $httpStatusCode)
{
throw new \Exception($reponse,$httpStatusCode);
}
}
curl_close($ch);
return $reponse;
}
public static function curlDelete($url, $postFields = null)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_FAILONERROR, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
//https 请求
if(strlen($url) > 5 && strtolower(substr($url,0,5)) == "https" ) {
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
}
if (is_array($postFields) && 0 < count($postFields))
{
$header = array("Content-Type: application/json; charset=utf-8", "Content-Length:".strlen(json_encode($postFields)));
curl_setopt($ch,CURLOPT_HTTPHEADER,$header);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($postFields));
}
$reponse = curl_exec($ch);
if (curl_errno($ch))
{
throw new \Exception(curl_error($ch),0);
}
else
{
$httpStatusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (200 !== $httpStatusCode)
{
throw new \Exception($reponse,$httpStatusCode);
}
}
curl_close($ch);
return $reponse;
}
//POST请求的结果解析
public static function decodePostResult($url, $param, $header = [], $hasFile = false)
{
$resp = [];
try {
$resp = json_decode(self::curlPost($url, $param, $header, $hasFile), true);
$resp['resp_status'] = empty($resp) ? false :true;
} catch (\Exception $e) {
$resp = json_decode($e->getMessage(), true);
$resp['resp_status'] = false;
}
return $resp;
}
//GET请求结果解析
public static function decodeGetResult($url, $header=[], $param = [])
{
$resp = [];
try {
$resp = json_decode(self::curlGet($url, $param, $header), true);
$resp['resp_status'] = empty($resp) ? false :true;
} catch (\Exception $e) {
$resp = json_decode($e->getMessage(), true);
$resp['resp_status'] = false;
}
return $resp;
}
}

140
app/service/Image.php Executable file
View File

@ -0,0 +1,140 @@
<?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 = public_path() . ltrim($src,'/');
if(is_file($realPath)){
$img = TImage::open($realPath);
list($img_w,$img_h) = $img->size();
if($max > 0 && $img_w > $max){
$height = floor($max * ($img_h / $img_w));
$img->thumb($max, $height)->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'] . DIRECTORY_SEPARATOR . $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 '';
}
//文件存在 就不用切割了
$suffix = explode(".",$src);
if(count($suffix)==2){
$cuttingUrl = $suffix[0]."_".$width."_".$height.".".$suffix[1];
if(file_exists(public_path().$cuttingUrl)){
return $cuttingUrl;
}
}
$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']. DIRECTORY_SEPARATOR .$info['filename'].'_'.$width.'_'.$height.'.'.$info['extension'];
$realThumbName = $rootPath . 'public/' . ltrim($thumbName, '/');
$realThumbName = str_replace('\\', '/', $realThumbName);
if(!file_exists($realThumbName)){
$image = TImage::open($realPath);
$image->thumb($width, $height, $type)->save($realThumbName);
}
return str_replace('\\', '/', $thumbName);
}
}

133
app/service/Jwt.php Executable file
View File

@ -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 = 86400 * 3;//秒
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;
}
}
}

102
app/service/Tool.php Executable file
View File

@ -0,0 +1,102 @@
<?php
namespace app\service;
use app\model\Category;
use app\model\Article;
class Tool
{
//删除文件
public static function delFile($path)
{
if(!empty(trim($path))){
$realPath = app()->getRootPath() . ltrim($path, '/');
if (file_exists($realPath)) {
$info = pathinfo($realPath);
$source = $info['dirname'] . DIRECTORY_SEPARATOR . $info['filename'] . '*.' . $info['extension'];
foreach(glob($source) as $filename){
if(is_file($filename)){
unlink($filename);
}
}
clearstatcache();// 清除缓存
}
}
}
/**
* 删除目录下的所有文件和子目录
* 调用完毕后请用clearstatcache()清理缓存
*/
public static function removeByPath($path)
{
if(is_dir($path)) {
if($handle = @opendir($path)) {
while (($file = readdir($handle)) !== false){
if($file != '.' && $file != '..') {
$child = $path.'/'.$file;
is_dir($child) ? self::removeByPath($child) : @unlink($child);
}
}
}
closedir($handle);
} elseif (is_file($path)) {
@unlink($path);
} else {
return false;
}
return true;
}
//去除字符串空格
public static function trimSpace($str)
{
return str_replace(' ', '', trim($str));
}
//获取文章链接
public static function getArticleUrl($article)
{
if(empty($article)){
return '';
}
// if(!empty($article['link'])){
// return $article['link'];
// }
if(empty($article['route'])){
return url('article/detail', ['id' => $article['id']]);
}
$category = Category::getById($article['category_id']);
$categoryRoute = '';
if(isset($category['route']) && !empty($category['route'])){
$categoryRoute = $category['route'];
}
$url = '/';
if(!empty($categoryRoute)){
$url .= $categoryRoute . '/';
}
return $url . $article['route'] . '.html';
}
/**
* 格式化金钱字段
* @param $money
* @return float
*/
public static function moneyFormat($money)
{
$money = is_string($money) ? trim($money) : $money;
return is_numeric($money) ? number_format($money, 2, '.', '') : 0;
}
// 获取客户端IP
public static function get_client_ip() {
$forwarded = request()->header("x-forwarded-for");
if($forwarded){
$ip = explode(',',$forwarded)[0];
}else{
$ip = request()->ip();
}
return $ip;
}
}

View File

@ -0,0 +1,66 @@
<?php
namespace app\service\driver;
use think\App;
use think\session\driver\File;
/**
* 自定义session file 驱动器
*
* 用于修复TP 自带file驱动器bug
*
* Class sessionFile
* @package app\service\driver
*/
class SessionFile extends File
{
public function __construct(App $app, array $config = [])
{
parent::__construct($app, $config);
}
/**
* bug v20220302 error: SplFileInfo::getMTime(): stat failed for filePath(xxx)
* 修复此函数
*
* Session 垃圾回收
* @access public
* @return void
*/
public function gc(): void
{
$lifetime = $this->config['expire'];
$now = time();
$files = $this->findFiles($this->config['path'], function (\SplFileInfo $item) use ($lifetime, $now) {
// 添加文件是否存在判断
if (!$item->isFile()) {
return false;
}
return $now - $lifetime > $item->getMTime();
});
foreach ($files as $file) {
$this->unlink($file->getPathname());
}
}
/**
* 重构unlink方法
*
* 判断文件是否存在后,删除
*
* @access private
* @param string $file
* @return bool
*/
protected function unlink(string $file): bool
{
return is_file($file) && unlink($file);
}
}

74
app/validate/Article.php Executable file
View File

@ -0,0 +1,74 @@
<?php
namespace app\validate;
use think\Validate;
class Article extends Validate
{
protected $rule = [
'category_id|栏目' => 'require|>:0',
'title|标题' => 'require|max:255',
'summary|摘要' => 'max:255',
'route|路由' => "routeValidate"
];
protected $message = [
'model_id' => '所属模型必需选择',
'route.routeValidate' => '路由不符合指定规范',
'route.unique' => '路由已存在',
];
protected function routeValidate($value)
{
$chars =preg_replace('/\s+/','',
"/^(
[\/]{1}
(
(
([A-Za-z0-9_-]*[A-Za-z_-]+)
|
([A-Za-z_-]+[A-Za-z0-9_-]*)
)
|
(
([A-Za-z0-9_-]+[A-Za-z_-]+[A-Za-z0-9_-]+)
|
([A-Za-z_-]+[A-Za-z0-9_-]+[A-Za-z_-]+)
)
)
)+
$/");
if ( preg_match($chars, $value)){
return true;
}else{
return "路由不符合指定规范";
}
}
// edit 验证场景定义
public function sceneEdit()
{
$id = input('id/d', 0);
return $this->append('route', 'unique:article,route,' .$id . '|unique:category,route' . '|unique:special_route,route' );
}
public function sceneAdd()
{
return $this->append('route', 'unique:category,route|unique:article,route|unique:special_route,route');
}
}

17
app/validate/AuthGroup.php Executable file
View File

@ -0,0 +1,17 @@
<?php
namespace app\validate;
use think\Validate;
class AuthGroup extends Validate
{
protected $rule = [
'title' => 'require',
'status' => 'require|number',
];
protected $message = [
'title.require' => '角色名称不能为空',
'status.require' => '角色状态必须设置',
'status.number' => '角色状态参数值只能为数字类型',
];
}

19
app/validate/AuthRule.php Executable file
View File

@ -0,0 +1,19 @@
<?php
namespace app\validate;
use think\Validate;
class AuthRule extends Validate
{
protected $rule = [
'title' => 'require',
'name' => 'require',
'status' =>'require|number',
];
protected $message = [
'title.require' => '名称必须',
'name.require'=> '标识必须',
'status.require'=> '显示状态必须传值',
'status.number'=> '显示状态传值只能为数字表示',
];
}

16
app/validate/Block.php Executable file
View File

@ -0,0 +1,16 @@
<?php
namespace app\validate;
use think\Validate;
class Block extends Validate
{
protected $rule = [
'title' => 'require',
'keyword' => 'require',
];
protected $message = [
'title.require' => '名称必须',
'keyword.require' => '键值必须'
];
}

79
app/validate/Category.php Executable file
View File

@ -0,0 +1,79 @@
<?php
namespace app\validate;
use think\Validate;
class Category extends Validate
{
protected $rule = [
'parent_id|父级分类' => 'require|number',
'model_id|所属模型' => 'require|number|gt:0',
'title|标题' => 'require|max:100',
'description|描述' => 'max:255',
'route|路由' =>"routeValidate",
'category_key|标识' =>"alphaDash",
];
protected $message = [
'model_id' => '所属模型必需选择',
'route.routeValidate' => '路由不符合指定规范',
'route.unique' => '路由已存在',
];
protected function routeValidate($value)
{
$chars =preg_replace('/\s+/','',
"/^(
[\/]{1}
(
(
([A-Za-z0-9_-]*[A-Za-z_-]+)
|
([A-Za-z_-]+[A-Za-z0-9_-]*)
)
|
(
([A-Za-z0-9_-]+[A-Za-z_-]+[A-Za-z0-9_-]+)
|
([A-Za-z_-]+[A-Za-z0-9_-]+[A-Za-z_-]+)
)
)
)+
$/");
if ( preg_match($chars, $value)){
return true;
}else{
return "路由不符合指定规范";
}
}
// edit 验证场景定义
public function sceneEdit()
{
$id = input('id/d', 0);
return $this->append('route', 'unique:category,route,' .$id . '|unique:article,route' )
->append("category_key",'unique:category,category_key,' .$id );
}
public function sceneAdd()
{
return $this->append('route', 'unique:category,route|unique:article,route|unique:special_route,route')
->append("category_key",'unique:category,category_key' );
}
}

View File

@ -0,0 +1,20 @@
<?php
namespace app\validate;
use think\Validate;
class DownloadValidate extends Validate
{
protected $rule = [
'title' => 'require|length:1,60',
'file' => 'require',
'visible' => 'require|in:0,1',
];
protected $message = [
'title.require' => '标题不能为空',
'file.require' => '文件必传',
'visible.require' => '状态必须设置',
'visible.in' => '状态参数错误',
];
}

21
app/validate/History.php Executable file
View File

@ -0,0 +1,21 @@
<?php
namespace app\validate;
use think\Validate;
class History extends Validate
{
protected $rule = [
'title' => 'require|length:1,60',
'visible' => 'require|in:0,1',
'event' => 'require',
];
protected $message = [
'title.require' => '标题不能为空',
'name.length' => '标题长度限制为60个字符以内',
'event.require' => '事件不能为空',
'visible.require' => '历程状态必须设置',
'visible.in' => '状态参数错误',
];
}

19
app/validate/HistoryInfo.php Executable file
View File

@ -0,0 +1,19 @@
<?php
namespace app\validate;
use think\Validate;
class HistoryInfo extends Validate
{
protected $rule = [
'title' => 'require|length:1,2000',
'visible' => 'require|in:0,1',
];
protected $message = [
'title.require' => '事例概述内容不能为空',
'name.length' => '事例概述内容长度限制为2000个字符以内',
'visible.require' => '历程事例状态必须设置',
'visible.in' => '状态参数错误',
];
}

20
app/validate/HonourValidate.php Executable file
View File

@ -0,0 +1,20 @@
<?php
namespace app\validate;
use think\Validate;
class HonourValidate extends Validate
{
protected $rule = [
'title' => 'require|length:1,60',
'visible' => 'require|in:0,1',
'image' => 'require',
];
protected $message = [
'title.require' => '标题不能为空',
'image.require' => '图片不能为空',
'visible.require' => '状态必须设置',
'visible.in' => '状态参数错误',
];
}

16
app/validate/Link.php Executable file
View File

@ -0,0 +1,16 @@
<?php
namespace app\validate;
use think\Validate;
class Link extends Validate
{
protected $rule = [
'title' => 'require',
'url' => 'url',
];
protected $message = [
'title.require' => '名称必须',
'url.url' => '请填写有效的网址,以http://或https://开头'
];
}

17
app/validate/LinkProduct.php Executable file
View File

@ -0,0 +1,17 @@
<?php
namespace app\validate;
use think\Validate;
class LinkProduct extends Validate
{
protected $rule = [
'title' => 'require',
'url' => 'url',
];
protected $message = [
'title.require' => '名称必须',
'url.require' => '链接网址不能为空以http://或https://开头',
'url.url' => '请填写有效的网址,以http://或https://开头'
];
}

Some files were not shown because too many files have changed in this diff Show More