This commit is contained in:
蒋俊鸿 2025-02-13 16:21:56 +08:00
parent 7c0cb8f2a4
commit 096ec5485f
6071 changed files with 1209860 additions and 0 deletions

20
.example.env Normal file
View File

@ -0,0 +1,20 @@
APP_DEBUG = true
APP_HOST = http://localhost
[APP]
DEFAULT_TIMEZONE = Asia/Shanghai
LAZY_LOAD_SIZE =
[DATABASE]
TYPE = mysql
HOSTNAME = 127.0.0.1
DATABASE = huo_cms
USERNAME = root
PASSWORD =
HOSTPORT = 3306
CHARSET = utf8mb4
DEBUG = true
PREFIX = hc_
[LANG]
default_lang = zh

11
.gitignore vendored Normal file
View File

@ -0,0 +1,11 @@
.env
/.idea
/.vscode
/.Ds.store
*.log
*.DS_Store
runtime
.VSCodeCounter/
composer.lock
/public/nginx.htaccess
/public/.htaccess

42
.travis.yml Normal file
View File

@ -0,0 +1,42 @@
sudo: false
language: php
branches:
only:
- stable
cache:
directories:
- $HOME/.composer/cache
before_install:
- composer self-update
install:
- composer install --no-dev --no-interaction --ignore-platform-reqs
- zip -r --exclude='*.git*' --exclude='*.zip' --exclude='*.travis.yml' ThinkPHP_Core.zip .
- composer require --update-no-dev --no-interaction "topthink/think-image:^1.0"
- composer require --update-no-dev --no-interaction "topthink/think-migration:^1.0"
- composer require --update-no-dev --no-interaction "topthink/think-captcha:^1.0"
- composer require --update-no-dev --no-interaction "topthink/think-mongo:^1.0"
- composer require --update-no-dev --no-interaction "topthink/think-worker:^1.0"
- composer require --update-no-dev --no-interaction "topthink/think-helper:^1.0"
- composer require --update-no-dev --no-interaction "topthink/think-queue:^1.0"
- composer require --update-no-dev --no-interaction "topthink/think-angular:^1.0"
- composer require --dev --update-no-dev --no-interaction "topthink/think-testing:^1.0"
- zip -r --exclude='*.git*' --exclude='*.zip' --exclude='*.travis.yml' ThinkPHP_Full.zip .
script:
- php think unit
deploy:
provider: releases
api_key:
secure: TSF6bnl2JYN72UQOORAJYL+CqIryP2gHVKt6grfveQ7d9rleAEoxlq6PWxbvTI4jZ5nrPpUcBUpWIJHNgVcs+bzLFtyh5THaLqm39uCgBbrW7M8rI26L8sBh/6nsdtGgdeQrO/cLu31QoTzbwuz1WfAVoCdCkOSZeXyT/CclH99qV6RYyQYqaD2wpRjrhA5O4fSsEkiPVuk0GaOogFlrQHx+C+lHnf6pa1KxEoN1A0UxxVfGX6K4y5g4WQDO5zT4bLeubkWOXK0G51XSvACDOZVIyLdjApaOFTwamPcD3S1tfvuxRWWvsCD5ljFvb2kSmx5BIBNwN80MzuBmrGIC27XLGOxyMerwKxB6DskNUO9PflKHDPI61DRq0FTy1fv70SFMSiAtUv9aJRT41NQh9iJJ0vC8dl+xcxrWIjU1GG6+l/ZcRqVx9V1VuGQsLKndGhja7SQ+X1slHl76fRq223sMOql7MFCd0vvvxVQ2V39CcFKao/LB1aPH3VhODDEyxwx6aXoTznvC/QPepgWsHOWQzKj9ftsgDbsNiyFlXL4cu8DWUty6rQy8zT2b4O8b1xjcwSUCsy+auEjBamzQkMJFNlZAIUrukL/NbUhQU37TAbwsFyz7X0E/u/VMle/nBCNAzgkMwAUjiHM6FqrKKBRWFbPrSIixjfjkCnrMEPw=
file:
- ThinkPHP_Core.zip
- ThinkPHP_Full.zip
skip_cleanup: true
on:
tags: true

201
LICENSE Normal file
View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

32
LICENSE.txt Normal 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.

22
app/AppService.php Normal file
View File

@ -0,0 +1,22 @@
<?php
declare (strict_types = 1);
namespace app;
use think\Service;
/**
* 应用服务类
*/
class AppService extends Service
{
public function register()
{
// 服务注册
}
public function boot()
{
// 服务启动
}
}

118
app/BaseController.php Normal file
View File

@ -0,0 +1,118 @@
<?php
declare (strict_types = 1);
namespace app;
use think\App;
use think\Container;
use think\exception\HttpResponseException;
use think\exception\ValidateException;
use think\Response;
use think\Validate;
/**
* 控制器基础类
*/
abstract class BaseController
{
/**
* Request实例
* @var \think\Request
*/
protected $request;
/**
* 应用实例
* @var \think\App
*/
protected $app;
/**
* 是否批量验证
* @var bool
*/
protected $batchValidate = false;
/**
* 构造方法
* @access public
* @param App $app 应用对象
*/
public function __construct(App $app)
{
$this->app = $app;
$this->request = $this->app->request;
// 控制器初始化
$this->initialize();
}
// 初始化
protected function initialize()
{
// 允许跨域
$this->cors();
}
public function cors()
{
header("access-control-allow-headers: Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,Login-Agent,X-Mx-ReqToken,X-Requested-With");
header("access-control-allow-methods: GET, POST, PUT, DELETE, HEAD, OPTIONS,PATCH");
header("access-control-allow-credentials: true");
header("access-control-allow-origin: *");
}
/**
* 验证数据
* @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, '.')) {
// 支持场景
[$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);
}
/**
* 获取当前的response 输出类型
* @access protected
* @return string
*/
protected function getResponseType(): string
{
if (!$this->app) {
$this->app = Container::get('app');
}
$isAjax = $this->request->isAjax();
return $isAjax || $this->request->isJson()
? 'json'
: 'html';
}
}

82
app/ExceptionHandle.php Normal file
View File

@ -0,0 +1,82 @@
<?php
namespace app;
use app\exception\AuthException;
use app\exception\BadSysSettingException;
use app\exception\BaseException;
use app\exception\ModelException;
use app\exception\ModelNotUniqueException;
use app\exception\TokenException;
use think\db\exception\DataNotFoundException;
use think\db\exception\ModelNotFoundException;
use think\exception\Handle;
use think\exception\HttpException;
use think\exception\HttpResponseException;
use think\exception\ValidateException;
use think\facade\Log;
use think\Response;
use Throwable;
/**
* 应用异常处理类
*/
class ExceptionHandle extends Handle
{
/**
* 不需要记录信息(日志)的异常类列表
* @var array
*/
protected $ignoreReport = [
HttpException::class,
HttpResponseException::class,
ModelNotFoundException::class,
DataNotFoundException::class,
ValidateException::class,
];
/**
* 记录异常信息(包括日志或者其它方式记录)
*
* @access public
* @param Throwable $exception
* @return void
*/
public function report(Throwable $exception): void
{
// 使用内置的方式记录异常日志
parent::report($exception);
}
/**
* Render an exception into an HTTP response.
*
* @access public
* @param \think\Request $request
* @param Throwable $e
* @return Response
*/
public function render($request, Throwable $e): Response
{
// 详细错误日志写入文件
Log::error($e->getMessage());
$str = $this->formatterTraceStr($e);
Log::error($str);
if(request()->isAjax() || request()->isJson() || env('APP_DEBUG') == 1){
// 添加自定义异常处理机制
return jsonReturn($e->getCode() ?: -1, $e->getMessage());
}
// 其他错误交给系统处理
return parent::render($request, $e);
}
public function formatterTraceStr(Throwable $e): string
{
$str = "=========手动记录日志=============\n" ;
$str .= '请求的API是【' . request()->url() . "\n";
$str .= '请求携带的参数是【' . json_encode(request() -> param()) . "\n";
$str .= $e->getTraceAsString();
$str .= "\n".date('Y-m-d H:i:s') . "】=========日志记录结束=============";
return $str;
}
}

309
app/Plugin.php Normal file
View File

@ -0,0 +1,309 @@
<?php
namespace app;
use app\model\AdminMenu;
use think\facade\Cache;
use think\facade\Db;
use think\View;
/**
* 插件类
*/
abstract class Plugin
{
/**
* 视图实例对象
* @var view
* @access protected
*/
private $view = null;
public static $vendorLoaded = [];
/**
* $info = array(
* 'name'=>'HelloWorld',
* 'title'=>'HelloWorld',
* 'description'=>'HelloWorld',
* 'status'=>1,
* 'author'=>'ThinkCMF',
* 'version'=>'1.0'
* )
*/
public $info = [];
private $pluginPath = '';
private $name = '';
private $configFilePath = '';
private $themeRoot = "";
/**
* Plugin constructor.
*/
public function __construct()
{
$this->name = $this->getName();
$nameCStyle = parse_name($this->name);
$this->pluginPath = CMS_ROOT . 'plugins/' . $nameCStyle . '/';
$this->configFilePath = $this->pluginPath . 'config.php';
if (empty(self::$vendorLoaded[$this->name])) {
$pluginVendorAutoLoadFile = $this->pluginPath . 'vendor/autoload.php';
if (file_exists($pluginVendorAutoLoadFile)) {
require_once $pluginVendorAutoLoadFile;
}
self::$vendorLoaded[$this->name] = true;
}
$config = $this->getConfig();
$theme = isset($config['theme']) ? $config['theme'] : '';
//$depr = "/";
$root = hcGetRoot();
$themeDir = empty($theme) ? "" : '/' . $theme;
$themePath = 'view' . $themeDir;
$this->themeRoot = $this->pluginPath . $themePath . '/';
$pluginRoot = "plugins/{$nameCStyle}";
$cmfAdminThemePath = config('template.cmf_admin_theme_path');
$cmfAdminDefaultTheme = config('template.cmf_admin_default_theme');
$adminThemePath = "{$cmfAdminThemePath}{$cmfAdminDefaultTheme}";
$replaceConfig = [
'__ROOT__' => $root,
'__PLUGIN_TMPL__' => $root . '/' . $pluginRoot . '/' . $themePath,
'__PLUGIN_ROOT__' => $root . '/' . $pluginRoot,
'__ADMIN_TMPL__' => "{$root}/{$adminThemePath}",
'__STATIC__' => "{$root}/static",
'__WEB_ROOT__' => $root
];
$app = app();
$view = new View($app);
$this->view = $view;
$this->view->engine()->config([
'view_base' => $this->themeRoot,
'tpl_replace_string' => $replaceConfig
]);
//加载多语言
$langSet = $app->lang->getLangSet();
$lang_file = $this->pluginPath . "lang/" . $langSet . ".php";
$app->lang->load($lang_file);
}
/**
* 加载模板输出
* @access protected
* @param string $template 模板文件名
* @return string
* @throws \Exception
*/
final protected function fetch($template)
{
if (!is_file($template)) {
$engineConfig = config('view');
$template = $this->themeRoot . $template . '.' . $engineConfig['view_suffix'];
}
// 模板不存在 抛出异常
if (!is_file($template)) {
throw new TemplateNotFoundException('template not exists:' . $template, $template);
}
return $this->view->fetch($template);
}
/**
* 渲染内容输出
* @access protected
* @param string $content 模板内容
* @return mixed
*/
final protected function display($content = '')
{
return $this->view->display($content);
}
/**
* 模板变量赋值
* @access protected
* @param mixed $name 要显示的模板变量
* @param mixed $value 变量的值
* @return void
*/
final protected function assign($name, $value = '')
{
$this->view->assign($name, $value);
}
/**
* 获取插件名
* @return string
*/
final public function getName()
{
if (empty($this->name)) {
$class = get_class($this);
$this->name = substr($class, strrpos($class, '\\') + 1, -6);
}
return $this->name;
}
/**
* 检查插件信息完整性
* @return bool
*/
final public function checkInfo()
{
$infoCheckKeys = ['name', 'title', 'description', 'status', 'author', 'version'];
foreach ($infoCheckKeys as $value) {
if (!array_key_exists($value, $this->info))
return false;
}
return true;
}
/**
* 获取插件根目录绝对路径
* @return string
*/
final public function getPluginPath()
{
return $this->pluginPath;
}
/**
* 获取插件配置文件绝对路径
* @return string
*/
final public function getConfigFilePath()
{
return $this->configFilePath;
}
/**
*
* @return string
*/
final public function getThemeRoot()
{
return $this->themeRoot;
}
/**
* @return View
*/
public function getView()
{
return $this->view;
}
/**
* 获取插件的配置数组
* @return array
*/
final public function getConfig()
{
$name = $this->getName();
if (PHP_SAPI != 'cli') {
static $_config = [];
if (isset($_config[$name])) {
return $_config[$name];
}
}
$pluginCofingKey = 'cmf_'.$name.'_plugin_config';
if (Cache::has($pluginCofingKey)){
return Cache::get($pluginCofingKey);
}
$ttl = mt_rand(600,6000);
$config = Db::name('plugin')->cache('cmf_'.$name.'_plugin_config_db',$ttl)->where('name', $name)->value('config');
if (!empty($config) && $config != "null") {
$config = json_decode($config, true);
} else {
$config = $this->getDefaultConfig();
}
Cache::set($pluginCofingKey,$config,$ttl);
$_config[$name] = $config;
return $config;
}
/**
* 获取插件的配置数组
* @return array
*/
final public function getDefaultConfig()
{
$config = [];
if (file_exists($this->configFilePath)) {
$tempArr = include $this->configFilePath;
if (!empty($tempArr) && is_array($tempArr)) {
foreach ($tempArr as $key => $value) {
if ($value['type'] == 'group') {
foreach ($value['options'] as $gkey => $gvalue) {
foreach ($gvalue['options'] as $ikey => $ivalue) {
$config[$ikey] = $ivalue['value'];
}
}
} else {
$config[$key] = $tempArr[$key]['value'];
}
}
}
}
return $config;
}
protected function installSql()
{
//执行安装sql
$dbConfig = env();
$sqlArr = hcSplitSql($this->pluginPath . '/install.sql', $dbConfig['DATABASE_PREFIX'], $dbConfig['DATABASE_CHARSET']);
$db = Db::connect();
foreach ($sqlArr as $sql) {
$sqlToExec = $sql . ';';
$result = sp_execute_sql($db, $sqlToExec);
if (!empty($result['error'])) {
return false;
}
}
return true;
}
public function editMenu($type = 1, $remark = '') {
$adminMenuModel = new AdminMenu();
if ($type == 1) { //安装
$adminMenuModel->where('remark', $remark)->update(['status' => 1]);
} else {//卸载
$adminMenuModel->where('remark', $remark)->update(['status' => 2]);
}
}
//必须实现安装
abstract public function install();
//必须卸载插件方法
abstract public function uninstall();
}

8
app/Request.php Normal file
View File

@ -0,0 +1,8 @@
<?php
namespace app;
// 应用请求对象类
class Request extends \think\Request
{
}

101
app/command/BaiduTongji.php Normal file
View File

@ -0,0 +1,101 @@
<?php
namespace app\command;
use think\console\Command;
use think\console\Input;
use think\console\Output;
use think\facade\Db;
class BaiduTongji extends Command
{
protected function configure()
{
$this->setName('baiduTongji')
->setDescription('百度统计');
}
protected function execute(Input $input, Output $output)
{
set_time_limit(0);
ini_set('memory_limit', '512M');
/*************************************** 百度统计开始 ************************************************/
$url = 'http://api.baidu.com/json/tongji/v1/ReportService/getData';
$listUrl = 'https://api.baidu.com/json/tongji/v1/ReportService/getSiteList';
$accountList = Db::name('seo_account')->field('account,password,token')->where("type=1")->selectOrFail();
$insertData = [
'pv_count' => 0,
'uv_count' => 0,
'ip_count' => 0,
'avg_visit_time' => 0
];
$totalNum = 0;
foreach ($accountList as $vo) {
$header = [
'account_type' => '1',
'username' => $vo['account'],
'password' => $vo['password'],
'token' => $vo['token'],
];
$lists = curlPost($listUrl, json_encode(['header' => $header]));
$website = json_decode($lists['data'], true);
if (empty($website['header']['failures'])) {
$website = $website['body']['data']['0']['list'];
}
if (!empty($website)) {
foreach ($website as $k => $v) {
$totalNum++;
$info = curlPost($url, json_encode([
'body' => [
"site_id"=> $v['site_id'],
"method" => "overview/getOutline"
],
'header' => $header
]))['data'];
$info = json_decode($info, true);
if (empty($info['body']['data'])) {
continue;
}
$info = $info['body']['data']['0']['result'];
if (isset($info['items']['1'])) {
if (is_numeric($info['items']['1']['1'])) {
$insertData['pv_count'] += $info['items']['1']['1'];
}
if (is_numeric($info['items']['1']['2'])) {
$insertData['uv_count'] += $info['items']['1']['2'];
}
if (is_numeric($info['items']['1']['3'])) {
$insertData['ip_count'] += $info['items']['1']['3'];
}
if (is_numeric($info['items']['1']['5'])) {
$insertData['avg_visit_time'] += $info['items']['1']['5'];
}
}
}
}
}
if ($totalNum != 0) {
$insertData['avg_visit_time'] = round($insertData['avg_visit_time'] / $totalNum);
}
$insertData['count_date'] = date('Y-m-d', strtotime('-1 day'));
Db::name('baidu_tj_gather')->insert($insertData);
/*************************************** 百度统计结束 ************************************************/
}
}

View File

@ -0,0 +1,61 @@
<?php
declare (strict_types = 1);
namespace app\command;
use think\console\command\Make;
use think\console\input\Option;
use think\console\input\Argument;
class CreateController extends Make
{
protected $type = "Controller";
protected function configure()
{
// 指令配置
$this->setName('make:hc-controller')
->addArgument('name', Argument::OPTIONAL, "controller path")
->setDescription('create a HuoCms controller command');
}
protected function getStub(): string
{
$stubPath = __DIR__ . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR;
return $stubPath . 'controller.stub';
}
protected function getClassName(string $name): string
{
return parent::getClassName($name) . ($this->app->config->get('route.controller_suffix') ? 'Controller' : '');
}
protected function getNamespace(string $app): string
{
return parent::getNamespace($app) . '\\controller';
}
protected function buildClass(string $name)
{
$stub = file_get_contents($this->getStub());
$namespace = trim(implode('\\', array_slice(explode('\\', $name), 0, -1)), '\\');
$class = str_replace($namespace . '\\', '', $name);
$modelName = str_replace(($this->app->config->get('route.controller_suffix') ? 'Controller' : ''),'',$class);
$funcParam = lcfirst($modelName);
return str_replace(['{%className%}', '{%actionSuffix%}', '{%namespace%}', '{%app_namespace%}','{%modelInstance%}','{%modelName%}'], [
$class,
$this->app->config->get('route.action_suffix'),
$namespace,
$this->app->getNamespace(),
$funcParam,
$modelName,
], $stub);
}
}

View File

@ -0,0 +1,53 @@
<?php
declare (strict_types = 1);
namespace app\command;
use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\input\Option;
use think\console\Output;
class CreateLink extends Command
{
protected function configure()
{
// 指令配置
$this->setName('admin-view:link')
->setDescription('the app\command\createLink command');
}
protected function execute(Input $input, Output $output)
{
foreach ($this->links() as $link => $target) {
if (file_exists($link)) {
$output->writeln('<error>' . $link . ' already exists!</error>');
} else {
$this->createLink($target, $link);
$output->writeln('<info>' . $link . ' created successfully.</info>');
}
}
$output->writeln('The links have been created.');
}
protected function links(): array
{
return ['public/view' => app()->getRootPath() .'view/'];
}
public function createLink($target,$link): bool
{
if (! windows_os()) {
return symlink($target, $link);
}
$mode = is_directory($target) ? 'J' : 'H';
exec("mklink /{$mode} ".escapeshellarg($link).' '.escapeshellarg($target));
}
}

View File

@ -0,0 +1,28 @@
<?php
declare (strict_types = 1);
namespace app\command;
use think\console\command\Make;
use think\console\input\Argument;
class CreateModel extends Make
{
protected function configure()
{
// 指令配置
$this->setName('make:hc-model')
->addArgument('name', Argument::OPTIONAL, "model path")
->setDescription('create a new model class with five base function');
}
protected function getStub(): string
{
return __DIR__ . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'model.stub';
}
protected function getNamespace(string $app): string
{
return parent::getNamespace($app) . '\\model';
}
}

View File

@ -0,0 +1,59 @@
<?php
namespace app\command;
use app\command\service\Server;
use app\command\service\TaskServer;
use app\command\service\TongJiServer;
use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\input\Option;
use think\console\Output;
class WebDiagnosis extends Command
{
protected function configure()
{
$this->setName('seo:check')
->addArgument('action', Argument::OPTIONAL, "start|stop|restart|reload|status|connections", 'start')
->addOption('host', 'H', Option::VALUE_OPTIONAL, 'the host of workerman service.', null)
->addOption('port', 'p', Option::VALUE_OPTIONAL, 'the port of workerman service.', null)
->addOption('daemon', 'd', Option::VALUE_NONE, 'Run the workerman service in daemon mode.')
->setDescription('网站诊断服务器');
}
protected function execute(Input $input, Output $output)
{
$action = $input->getArgument('action');
if (DIRECTORY_SEPARATOR !== '\\') {
if (!in_array($action, ['start', 'stop', 'reload', 'restart', 'status', 'connections'])) {
$output->writeln("<error>Invalid argument action:{$action}, Expected start|stop|restart|reload|status|connections .</error>");
return false;
}
global $argv;
array_shift($argv);
array_shift($argv);
array_shift($argv);
array_unshift($argv, 'think', $action);
}
$logo = <<<EOL
_____ ______ ____ _____ _ _ ______ _____ _ __
/ ____ | ____ / __ \ / ____ | | | | | ____ / ____ | |/ /
| (___ | |__ | | | | | | | |__| | | |__ | | | ' /
\___ \ | __| | | | | | | | __ | | __| | | | <
____) | |___ | |__| | | |____ | | | | | |___ | |____ | . \
|_____/ |______ \____/ \_____ |_| |_| |______ \_____ |_|\_\
EOL;
$output->writeln($logo . PHP_EOL);
if (!(strtoupper(substr(PHP_OS,0,3))==='WIN')) {
TongJiServer::start();
TaskServer::start();
}
Server::start();
}
}

View File

@ -0,0 +1,55 @@
<?php
namespace app\command;
use app\command\service\Server;
use app\command\service\TaskServer;
use app\command\service\TongJiServer;
use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\input\Option;
use think\console\Output;
class WebDiagnosis2 extends Command
{
protected function configure()
{
$this->setName('seo:check2')
->addArgument('action', Argument::OPTIONAL, "start|stop|restart|reload|status|connections", 'start')
->addOption('host', 'H', Option::VALUE_OPTIONAL, 'the host of workerman service.', null)
->addOption('port', 'p', Option::VALUE_OPTIONAL, 'the port of workerman service.', null)
->addOption('daemon', 'd', Option::VALUE_NONE, 'Run the workerman service in daemon mode.')
->setDescription('网站诊断服务器');
}
protected function execute(Input $input, Output $output)
{
$action = $input->getArgument('action');
if (DIRECTORY_SEPARATOR !== '\\') {
if (!in_array($action, ['start', 'stop', 'reload', 'restart', 'status', 'connections'])) {
$output->writeln("<error>Invalid argument action:{$action}, Expected start|stop|restart|reload|status|connections .</error>");
return false;
}
global $argv;
array_shift($argv);
array_shift($argv);
array_shift($argv);
array_unshift($argv, 'think', $action);
}
$logo = <<<EOL
_____ ______ ____ _____ _ _ ______ _____ _ __
/ ____ | ____ / __ \ / ____ | | | | | ____ / ____ | |/ /
| (___ | |__ | | | | | | | |__| | | |__ | | | ' /
\___ \ | __| | | | | | | | __ | | __| | | | <
____) | |___ | |__| | | |____ | | | | | |___ | |____ | . \
|_____/ |______ \____/ \_____ |_| |_| |______ \_____ |_|\_\
EOL;
$output->writeln($logo . PHP_EOL);
TongJiServer::start();
}
}

View File

@ -0,0 +1,55 @@
<?php
namespace app\command;
use app\command\service\Server;
use app\command\service\TaskServer;
use app\command\service\TongJiServer;
use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\input\Option;
use think\console\Output;
class WebDiagnosis3 extends Command
{
protected function configure()
{
$this->setName('seo:check3')
->addArgument('action', Argument::OPTIONAL, "start|stop|restart|reload|status|connections", 'start')
->addOption('host', 'H', Option::VALUE_OPTIONAL, 'the host of workerman service.', null)
->addOption('port', 'p', Option::VALUE_OPTIONAL, 'the port of workerman service.', null)
->addOption('daemon', 'd', Option::VALUE_NONE, 'Run the workerman service in daemon mode.')
->setDescription('网站诊断服务器');
}
protected function execute(Input $input, Output $output)
{
$action = $input->getArgument('action');
if (DIRECTORY_SEPARATOR !== '\\') {
if (!in_array($action, ['start', 'stop', 'reload', 'restart', 'status', 'connections'])) {
$output->writeln("<error>Invalid argument action:{$action}, Expected start|stop|restart|reload|status|connections .</error>");
return false;
}
global $argv;
array_shift($argv);
array_shift($argv);
array_shift($argv);
array_unshift($argv, 'think', $action);
}
$logo = <<<EOL
_____ ______ ____ _____ _ _ ______ _____ _ __
/ ____ | ____ / __ \ / ____ | | | | | ____ / ____ | |/ /
| (___ | |__ | | | | | | | |__| | | |__ | | | ' /
\___ \ | __| | | | | | | | __ | | __| | | | <
____) | |___ | |__| | | |____ | | | | | |___ | |____ | . \
|_____/ |______ \____/ \_____ |_| |_| |______ \_____ |_|\_\
EOL;
$output->writeln($logo . PHP_EOL);
TaskServer::start();
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace app\command\service;
use Workerman\Protocols\Http\Request;
use \Workerman\Connection\AsyncTcpConnection;
use Workerman\Connection\TcpConnection;
use Workerman\Worker;
class Server
{
public static function start()
{
$http_worker = new Worker("http://0.0.0.0:19910");
$http_worker->count = 1;
$http_worker->name = 'task accept';
$http_worker->onMessage = function(TcpConnection $connection, Request $request)
{
$task_connection = new AsyncTcpConnection('Text://127.0.0.1:19890');
$task_connection->send(json_encode($request->post()));
$task_connection->connect();
$connection->send(json_encode(['code' => 0, 'data' => '', 'msg' => '任务投递成功']));
};
if(!defined('GLOBAL_START')) {
Worker::runAll();
}
}
}

View File

@ -0,0 +1,149 @@
<?php
namespace app\command\service;
use app\command\service\step\CheckIncluded;
use app\command\service\step\CheckKeywords;
use app\command\service\step\CheckLinks;
use app\command\service\step\CheckSSL301;
use app\command\service\step\CheckTplCode;
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
class TaskServer
{
public static $db;
public static function start()
{
$taskWorker = new Worker('Text://0.0.0.0:19890');
$taskWorker->count = 8;
$taskWorker->name = 'TaskWorker';
$taskWorker->onWorkerStart = function (Worker $worker)
{
self::$db = new \Workerman\MySQL\Connection(env('database.hostname'),
env('database.hostport', 3306), env('database.username'), env('database.password', ''), env('database.database', ''));
};
$taskWorker->onMessage = function(TcpConnection $connection, $taskData)
{
$taskData = json_decode($taskData, true);
try {
switch ($taskData['cmd']) {
case 'code':
$res = self::codeAnalysis($taskData);
break;
case 'web':
$res = self::webpageAnalysis($taskData);
break;
case 'keywords':
$res = self::keywordsAnalysis($taskData, self::$db);
break;
case 'links':
$res = self::linksAnalysis($taskData, self::$db);
break;
case 'article':
$res = self::articleAnalysis($taskData);
break;
}
self::$db->update(env('database.prefix') . 'seo_check_task_detail')
->cols([
'remark' => json_encode($res),
'status' => 2,
'update_time' => date('Y-m-d H:i:s')
])
->where('task_id=' . $taskData['task_id'] . ' AND code="' . $taskData['cmd'] . '"')->query();
$has = self::$db->select('id')->from(env('database.prefix') . 'seo_check_task_detail')
->where('task_id=' . $taskData['task_id'] . ' AND status=1')->row();
if (empty($has)) {
self::$db->update(env('database.prefix') . 'seo_check_task')->cols(array('status'=>'2'))->where("id={$taskData['task_id']}")->query();
}
} catch (\Exception $e) {
if (strpos($e->getMessage(), '404 Not Found') !== false || strpos($e->getMessage(), '503 Service Temporarily Unavailable') !== false) {
$connection->send(json_encode(['code' => -1, 'data' => [], 'msg' => '该网站暂时无法访问']));
} else {
$connection->send(json_encode(['code' => -1, 'data' => [], 'msg' => $e->getMessage()]));
}
return;
}
$connection->send(json_encode($res));
};
if (strtoupper(substr(PHP_OS,0,3))==='WIN') {
Worker::runAll();
}
}
/**
* 代码分析
* @param $taskData
* @return array
*/
private static function codeAnalysis($taskData)
{
$analysis = [];
// 检测301
$analysis301 = CheckSSL301::run($taskData);
// 检测模板代码
$analysisTplCode = CheckTplCode::run($taskData);
$analysis = $analysis + $analysis301 + $analysisTplCode;
return dataReturn(101, '代码分析完成', $analysis);
}
/**
* 网站收录分析
* @param $taskData
* @return array
*/
private static function webpageAnalysis($taskData)
{
$analysis = CheckIncluded::run($taskData);
return dataReturn(102, '收录分析完成', $analysis);
}
/**
* 关键词分析
* @param $taskData
* @param $db
* @return array
*/
private static function keywordsAnalysis($taskData, $db)
{
$analysis = CheckKeywords::run($taskData, $db);
return dataReturn(103, '关键词分析完成', $analysis);
}
/**
* 导入链接分析
* @param $taskData
* @param $db
* @return array
*/
private static function linksAnalysis($taskData, $db)
{
$analysis = CheckLinks::run($taskData, $db);
return dataReturn(104, '导入链接分析完成', $analysis);
}
/**
* 文章分析
* @param $taskData
* @return array
*/
private static function articleAnalysis($taskData)
{
return dataReturn(105, '文章分析完成');
}
}

View File

@ -0,0 +1,171 @@
<?php
namespace app\command\service;
use think\facade\Log;
use Workerman\Timer;
use Workerman\Worker;
class TongJiServer
{
public static function start()
{
$task = new Worker();
$task->count = 1;
$task->name = 'pv collect';
$task->onWorkerStart = function($task)
{
$db = new \Workerman\MySQL\Connection(env('database.hostname'),
env('database.hostport', 3306), env('database.username'), env('database.password', ''), env('database.database', ''));
/*************************************** 站长之家排名开始 ************************************************/
$baiduPcUrl = 'https://apidatav2.chinaz.com/single/baidupc/keywordranking';
$baiduMobileUrl = 'https://apidatav2.chinaz.com/single/baidumobile/keywordranking';
$pc360Url = 'https://apidatav2.chinaz.com/single/sopc/keywordranking';
$sogouMobileUrl = 'https://apidatav2.chinaz.com/single/sogoumobile/keywordranking';
file_put_contents('./last_rum_time.log', time()); // 上次执行时间
$info = $db->select('value')->from(env('database.prefix') . 'sys_setting')
->where('title="chinaz_token" and seller_id=1')->row();
if (!empty($info)) {
$token = $info['value'];
Timer::add(3600, function () use ($db, $token, $baiduPcUrl, $baiduMobileUrl, $pc360Url, $sogouMobileUrl) {
if (date('H:i') >= '11:52' || date('H:i') < '11:53') {
$frequency = $db->select('value')->from(env('database.prefix') . 'sys_setting')
->where('title="keywords_search_time" and seller_id=1')->row();
$lastRunTime = file_get_contents('./last_rum_time.log');
$canRun = false;
if ($frequency == 'week') {
if ((time() - $lastRunTime) / 86400 >= 7) {
$canRun = true;
file_put_contents('./last_rum_time.log', time());
}
} elseif($frequency == 'day' && (time() - $lastRunTime) >= 86400) {
$canRun = true;
file_put_contents('./last_rum_time.log', time());
} else {
$canRun = true;
file_put_contents('./last_rum_time.log', time());
}
Log::info('可执行状态:' . $canRun);
try {
if ($canRun) {
Log::info('执行统计开始' . $canRun);
$keywords = $db->select('id,name,url,website_id,seller_id')->from(env('database.prefix') . 'keyword')
->where('status=1 and is_del=1')->query();
foreach ($keywords as $key => $vo) {
$updateData = [
'baidu_pc' => 0,
'baidu_mob' => 0,
'three_pc' => 0,
'sougou_mob' => 0
];
// 百度PC
$res = curlPost($baiduPcUrl, [
'key' => $token,
'domain' => $vo['url'],
'keyword' => $vo['name'],
'top100' => true
])['data'];
Log::info($vo['name'] . ' 百度PC res>>>>>>>:' . $res);
$res = json_decode($res, true);
if (!empty($res['Result']['Ranks'])) {
$rank = explode('-', $res['Result']['Ranks']['0']['RankStr']);
$updateData['baidu_pc'] = ($rank[0] - 1) * 10 + $rank['1'];
self::writeKeywordsQuery($db, $vo, $res, $rank, 'baidu_pc');
}
// 百度移动
$res = curlPost($baiduMobileUrl, [
'key' => $token,
'domain' => $vo['url'],
'keyword' => $vo['name']
])['data'];
Log::info($vo['name'] . ' 百度移动 res>>>>>>>:' . $res);
$res = json_decode($res, true);
if (!empty($res['Result']['Ranks'])) {
$rank = explode('-', $res['Result']['Ranks']['0']['RankStr']);
$updateData['baidu_mob'] = ($rank[0] - 1) * 10 + $rank['1'];
self::writeKeywordsQuery($db, $vo, $res, $rank, 'baidu_mob');
}
// 360pc
$res = curlPost($pc360Url, [
'key' => $token,
'domain' => $vo['url'],
'keyword' => $vo['name']
])['data'];
Log::info($vo['name'] . ' 360pc res>>>>>>>:' . $res);
$res = json_decode($res, true);
if (!empty($res['Result']['Ranks'])) {
$rank = explode('-', $res['Result']['Ranks']['0']['RankStr']);
$updateData['three_pc'] = ($rank[0] - 1) * 10 + $rank['1'];
self::writeKeywordsQuery($db, $vo, $res, $rank, 'three_pc');
}
// 搜狗移动
$res = curlPost($sogouMobileUrl, [
'key' => $token,
'domain' => $vo['url'],
'keyword' => $vo['name']
])['data'];
Log::info($vo['name'] . ' 搜狗移动 res>>>>>>>:' . $res);
$res = json_decode($res, true);
if (!empty($res['Result']['Ranks'])) {
$rank = explode('-', $res['Result']['Ranks']['0']['RankStr']);
$updateData['sougou_mob'] = ($rank[0] - 1) * 10 + $rank['1'];
self::writeKeywordsQuery($db, $vo, $res, $rank, 'sougou_mob');
}
$db->update(env('database.prefix') . 'keyword')->cols($updateData)->where('id=' . $vo['id'])->query();
}
}
} catch (\Exception $e) {
Log::error($e->getMessage() . '>>>>>>
' . $e->getTraceAsString());
}
Log::info('执行结束');
}
});
}
/*************************************** 站长之家排名结束 ************************************************/
};
if (strtoupper(substr(PHP_OS,0,3))==='WIN') {
Worker::runAll();
}
}
protected static function writeKeywordsQuery($db, $vo, $res, $rank, $engine)
{
$db->insert(env('database.prefix') . 'keyword_query')->cols([
'keyword_id' => $vo['id'],
'seller_id' => $vo['seller_id'],
'website_id' => $vo['website_id'],
'keyword' => $vo['name'],
'search_engine' => $engine,
'collect_count' => isset($res['Result']['SiteCount']) ? $res['Result']['SiteCount'] : 0,
'top_rank' => ($rank[0] - 1) * 10 + $rank['1'],
'create_time' => time(),
'page_title' => $res['Result']['Ranks']['0']['Title'] ?? '',
'page_url' => $res['Result']['Ranks']['0']['Url'] ?? '',
])->query();
}
}

View File

@ -0,0 +1,55 @@
<?php
namespace app\command\service\step;
use GuzzleHttp\Client;
class CheckIncluded
{
public static function run($taskData)
{
$included = [
'status' => 1,
'count' => 0
];
try {
$client = new Client();
$url = 'https://www.baidu.com/s?wd=site%3A' . $taskData['websiteUrl'];
$jar = new \GuzzleHttp\Cookie\CookieJar();
$userAgentMap = config('userAgent');
$userAgentKey = array_rand($userAgentMap);
$res = $client->request('GET', $url, [
'cookies' => $jar,
'headers' => [
'user-agent' => $userAgentMap[$userAgentKey]
],
]);
if ($res->getStatusCode() == 200) {
$html = \phpQuery::newDocumentHTML($res->getBody());
$hasNoInclude = $html['.content_none .nors']->html();
if (!empty($hasNoInclude)) {
$included = [
'status' => 2,
'count' => 0
];
} else {
$included = [
'status' => 1,
'count' => $html['#content_left .c-span-last p:eq(0) b']->text()
];
}
} else {
return $included;
}
} catch (\Exception $e) {
return $included;
}
return $included;
}
}

View File

@ -0,0 +1,124 @@
<?php
namespace app\command\service\step;
use think\facade\Db;
class CheckKeywords
{
public static function run($taskData, $db)
{
$analysis = [
'deadLink' => []
];
$keywords = $taskData['keywords'];
if (empty($keywords)) {
return [];
}
// 处理栏目页
$columnPages = $taskData['cate'];
$cateIds = [];
$cateId2Alias = [];
$opts = [
'http'=> [
'method' => "GET",
'timeout' => 10,
]
];
$context = stream_context_create($opts);
foreach ($columnPages as $vo) {
$cateIds[] = $vo['id'];
$cateId2Alias[$vo['id']] = $vo['alias'];
try {
$nowPageHtml = file_get_contents('http://' . $taskData['websiteUrl'] . $vo['alias'], false, $context);
echo "查询获取到页面数据" . PHP_EOL;
$totalWords = self::getCleanContent($nowPageHtml);
foreach ($keywords as $v) {
$analysis[$vo['alias']][$v['name']] = round((substr_count($totalWords, $v['name'])
* mb_strlen($v['name'])) / mb_strlen($totalWords), 4);
}
} catch (\Exception $e) {
echo "获取到死链" . 'http://' . $taskData['websiteUrl'] . $vo['alias'] . PHP_EOL;
$analysis['deadLink'][] = 'http://' . $taskData['websiteUrl'] . $vo['alias'];
}
}
// 处理文章页
if (!empty($cateIds)) {
$pageSize = 20;
$total = $db->select('count(*) as `c_total`')->from(env('database.prefix') . 'category_sub_content')
->where("category_id in(" . implode(',', $cateIds) . ")")->row()['c_total'];
$totalPage = ceil($total / $pageSize);
for ($nowPage = 1; $nowPage <= $totalPage; $nowPage++) {
$offset = ($nowPage - 1) * $pageSize;
$articles = $db->select('category_id,sub_content_id')->from(env('database.prefix') . 'category_sub_content')
->where("category_id in(" . implode(',', $cateIds) . ")")->limit($pageSize)->offset($offset)->query();
foreach ($articles as $vo) {
if (isset($cateId2Alias[$vo['category_id']])) {
$url = $cateId2Alias[$vo['category_id']] . '/' . $vo['sub_content_id'];
try {
$nowPageHtml = file_get_contents('http://' . $taskData['websiteUrl'] . $url, false, $context);
$totalWords = self::getCleanContent($nowPageHtml);
foreach ($keywords as $v) {
$analysis[$url][$v['name']] = round((substr_count($totalWords, $v['name']) * mb_strlen($v['name'])) / mb_strlen($totalWords), 4);
}
} catch (\Exception $e) {
$analysis['deadLink'][] = 'http://' . $taskData['websiteUrl'] . $url;
}
}
}
}
}
// 最后统计总数据
$analysisFinal = [];
foreach ($analysis as $key => $vo) {
if ($key != 'deadLink') {
$analysisFinal['keywords'][] = '路径 ' . $key . ' 的关键词密度是:' . round(array_sum($vo), 2);
} else {
$analysisFinal[$key] = $vo;
}
}
return $analysisFinal;
}
private static function getCleanContent($nowPage)
{
// 获取所有的img的alt
$html = \phpQuery::newDocument($nowPage);
$images = $html['img'];
$a = $html['a'];
// 去除js标签内的内容
$html['script']->html('');
$html['noscript']->html('');
$html['style']->html('');
$imagesAltMap = [];
foreach ($images as $img) {
if (!empty(trim(pq($img)->attr('alt')))) {
$imagesAltMap[] = trim(pq($img)->attr('alt'));
}
}
$aTitleMap = [];
foreach ($a as $item) {
if (!empty(trim(pq($item)->attr('title')))) {
$aTitleMap[] = trim(pq($item)->attr('title'));
}
}
$cleanWords = trim(str_replace(' ', '', str_replace(PHP_EOL, '', strip_tags($html))));
return $cleanWords . implode('', $imagesAltMap) . implode('', $aTitleMap);
}
}

View File

@ -0,0 +1,78 @@
<?php
namespace app\command\service\step;
use think\facade\Db;
class CheckLinks
{
public static function run($taskData, $db)
{
$links = $db->select('url,type,position')->from(env('database.prefix') . 'link')
->where("is_del=1 AND website_id = " . $taskData['websiteId'])->query();
$inLinks = [];
$outLinks = [];
foreach ($links as $vo) {
if ($vo['type'] == 1) {
try {
$html = file_get_contents($vo['position']);
if (strstr($html, $vo['url']) == false) {
$inLinks['linksHave'][] = [
'status' => 2,
'msg' => $vo['position'] . '的友链已经被删除'
];
} else {
$inLinks['linksHave'][] = [
'status' => 1,
'msg' => $vo['position'] . '的友链正常显示'
];
// 检测nofollow
$html = \phpQuery::newDocument($html);
if ($html['a[href="' . $vo['url'] . '"]']->attr('rel') === 'nofollow') {
$inLinks['nofollow'][] = [
'status' => 2,
'msg' => $vo['position'] . '的友链添加了nofollow'
];
} else {
$inLinks['nofollow'][] = [
'status' => 1,
'msg' => $vo['position'] . '的友链属性正常'
];
}
}
} catch (\Exception $e) {
$inLinks['linksHave'][] = [
'status' => 2,
'msg' => $vo['position'] . '无法访问'
];
}
} else if ($vo['type'] == 2) {
try {
file_get_contents($vo['url']);
$outLinks['linksHave'][] = [
'status' => 1,
'msg' => $vo['url'] . '可以正常访问'
];
} catch (\Exception $e) {
$outLinks['linksHave'][] = [
'status' => 2,
'msg' => $vo['url'] . '无法访问'
];
}
}
}
$analysis = [
'inlinks' => $inLinks,
'outlinks' => $outLinks
];
return $analysis;
}
}

View File

@ -0,0 +1,156 @@
<?php
namespace app\command\service\step;
use GuzzleHttp\Client;
class CheckSSL301
{
/**
* 检测 301 ssl
* @param $taskData
* @return array
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public static function run($taskData)
{
$analysis = [];
$urlMap = explode('.', $taskData['websiteUrl']);
$client = new Client();
// 二级域名的情况
if (count($urlMap) == 3 && $urlMap[0] != 'www') {
$analysis['c301'] = [
'status' => 2,
'msg' => '该网站使用的非一级域名,建议使用一级域名'
];
$analysis['ssl'] = self::requestWithHttps($client, $taskData);
return $analysis;
}
// 非二级域名的情况
if (count($urlMap) == 3 && $urlMap[0] == 'www') {
$url = 'http://' . $urlMap['1'] . '.' . $urlMap['2'];
$domain = $urlMap['1'] . '.' . $urlMap['2'];
} else {
$url = 'http://' . $taskData['websiteUrl'];
$domain = $taskData['websiteUrl'];
}
$userAgentMap = config('userAgent');
$userAgentKey = array_rand($userAgentMap);
$res = $client->request('GET', $url, [
'allow_redirects' => false,
'headers' => [
'user-agent' => $userAgentMap[$userAgentKey]
],
]);
// 做了 301 跳转的
if ($res->getStatusCode() == 301) {
// 进一步确认
$resHeaders = $res->getHeaders();
$location = $resHeaders['Location'][0];
if (strpos($location, 'https://') !== false) {
$analysis['ssl'] = [
'status' => 1,
'msg' => '该网站采用了安全的https协议'
];
} else {
$analysis['ssl'] = [
'status' => 2,
'msg' => '该网站没有采用了安全的https协议'
];
}
$ckDomain = strstr($location, 'www');
if ($ckDomain !== false) {
$analysis['c301'] = [
'status' => 1,
'msg' => '该网站采用正确的301跳转方式'
];
return $analysis;
}
// 进一步确认
if ($analysis['ssl']['status'] != 1) {
$analysis['c301'] = [
'status' => 2,
'msg' => '该网站没有采用正确的301跳转方式'
];
return $analysis;
}
// 去请求带https的www域名
$res2 = $client->request('GET', 'https://www.' . $domain, [
'allow_redirects' => false
]);
if ($res2->getStatusCode() == 200) {
$analysis['c301'] = [
'status' => 1,
'msg' => '该网站采用正确的301跳转方式'
];
} else {
$analysis['c301'] = [
'status' => 2,
'msg' => '该网站没有采用正确的301跳转方式'
];
}
return $analysis;
}
// 未做 301 跳转的
$analysis['c301'] = [
'status' => 2,
'msg' => '该网站没有采用正确的301跳转方式'
];
$analysis['ssl'] = self::requestWithHttps($client, $taskData);
return $analysis;
}
private static function requestWithHttps($client, $taskData)
{
$ssl = [];
$userAgentMap = config('userAgent');
$userAgentKey = array_rand($userAgentMap);
try {
$res2 = $client->request('GET', 'https://' . $taskData['websiteUrl'], [
'allow_redirects' => false,
'headers' => [
'user-agent' => $userAgentMap[$userAgentKey]
],
]);
if ($res2->getStatusCode() == 200) {
$ssl = [
'status' => 1,
'msg' => '该网站采用了安全的https协议'
];
} else {
$ssl = [
'status' => 2,
'msg' => '该网站没有采用了安全的https协议'
];
}
} catch (\Exception $e) {
$ssl = [
'status' => 2,
'msg' => '该网站没有采用了安全的https协议'
];
}
return $ssl;
}
}

View File

@ -0,0 +1,136 @@
<?php
namespace app\command\service\step;
use think\facade\App;
class CheckTplCode
{
public static function run($taskData)
{
$analysis = [
'hn' => [
'status' => 1,
'msg' => []
],
'tags' => [
'status' => 1,
'msg' => []
],
'robots' => [
'status' => 1,
'msg' => 'robots.txt文件正确'
],
'sitemap' => [
'status' => 1,
'msg' => 'sitemap文件放置正确'
],
'urlLevel' => [
'status' => 1,
'msg' => []
],
'c404' => [
'status' => 1,
'msg' => '404文件放置正确'
]
];
// 目录层级
foreach ($taskData['cate'] as $vo) {
if ($vo['alias'] != '/' && !empty($vo['alias'])) {
$level = count(array_filter(explode('/', $vo['alias'])));
if ($level >= 3) {
$analysis['urlLevel']['status'] = 2;
$analysis['urlLevel']['msg'][] = $vo['title'] . '页面url层级太深达到了' . $level . '层';
}
}
}
// 404文件
$rootPath = App::getRootPath() . 'public';
if (!is_file($rootPath . '/system_file/hc_error/404.html')) {
$analysis['c404']['status'] = 2;
$analysis['c404']['msg'] = '未正确放置404文件';
}
$path = $rootPath . '/themes/website/' . $taskData['websiteId'] . '/' . $taskData['lang'] . '/' . $taskData['theme'];
if (!is_dir($path)) {
return $analysis;
}
$files = scandir($path);
foreach ($files as $value){
if($value != '.' && $value != '..' && is_file($path . '/' . $value)){
$html = file_get_contents($path . '/' . $value);
// 存在ajax
if (strstr($html, '$.ajax') !== false || strstr($html, '$.getJSON') !== false
|| strstr($html, '$.post') !== false) {
$analysis['tags']['status'] = 2;
$analysis['tags']['msg']['ajax'][] = $value . '页面有ajax异步请求建议更换。';
}
$html = \phpQuery::newDocumentHTML($html);
// 页面为vue.js编写
$html['script']->html('');
$html['noscript']->html('');
$vueJs = str_replace(PHP_EOL, '', trim(strip_tags($html)));
if (empty($vueJs)) {
$analysis['tags']['status'] = 2;
$analysis['tags']['msg']['vuejs'][] = $value . '页面是Vue.js编写无法收录';
}
// 检测h1数量
$h1 = $html["h1"];
if (count($h1) >= 2) {
$analysis['hn']['status'] = 2;
$analysis['hn']['msg']['h1'][] = $value . '有' . count($h1) . '个<h1>标签,数量过多。';
}
// 检测iframe框架
$iframe = $html["iframe"];
if (count($iframe) > 0) {
$analysis['tags']['status'] = 2;
$analysis['tags']['msg']['iframe'][] = $value . '页面存在iframe标签建议更换。';
}
// 检测flash技术
$flash = $html["object"];
if (count($flash) > 0) {
$analysis['tags']['status'] = 2;
$analysis['tags']['msg']['flash'][] = $value . '页面存在flash标签建议更换。';
}
// alt标签
$imgs = $html["img"];
foreach($imgs as $imgObj) {
if (empty(pq($imgObj)->attr('alt'))) {
$analysis['tags']['status'] = 2;
$analysis['tags']['msg']['alt'][] = $value . '页面img标签alt属性缺少或为空';
break;
}
}
// robots 文件
if (!is_file($rootPath . '/robots.txt')) {
$analysis['robots'] = [
'status' => 2,
'msg' => '未在正确放置robots.txt文件'
];
}
// sitemap
if (!is_file($rootPath . '/xml/' . $taskData['websiteId'] . '_sitemap.xml')) {
$analysis['sitemap'] = [
'status' => 2,
'msg' => '未正确放置sitemap.xml文件'
];
}
}
}
return $analysis;
}
}

View File

@ -0,0 +1,138 @@
<?php
declare (strict_types = 1);
namespace {%namespace%};
use app\model\{%modelName%};
use app\validate\{%modelName%}Validate;
use think\exception\ValidateException;
class {%className%} extends BaseController
{
/**
* 显示资源列表
*
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function index{%actionSuffix%}({%modelName%} ${%modelInstance%}): \think\response\Json
{
$where = [
'seller_id' => $this->admin['seller_id'],
'is_del' => 1,
];
$limit = 10;
$limitParam = (int)input('limit');
if($limitParam){
$limit = $limitParam;
}
// TODO
// 添加其他逻辑
${%modelInstance%}List = ${%modelInstance%}->get{%modelName%}List($where,$limit);
return json(pageReturn(${%modelInstance%}List));
}
/**
* 保存新建的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function save{%actionSuffix%}({%modelName%} ${%modelInstance%}): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
// 数据验证
try{
validate({%modelName%}Validate::class)->scene('save')->check($param);
}catch(ValidateException $e){
return jsonReturn(-1, $e->getError());
}
// TODO
// 其他逻辑
$res = ${%modelInstance%} -> add{%modelName%}($param);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 显示指定的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \app\exception\ModelEmptyException
*/
public function read{%actionSuffix%}({%modelName%} ${%modelInstance%}): \think\response\Json
{
$id = (int)input('id');
if(!$id){
// TODO
// 修改错误消息
return jsonReturn(-1,'ErrorMsg');
}
$where = [
'id' => $id,
'seller_id' => $this->admin['seller_id']
];
// TODO
// 其他逻辑
$res = ${%modelInstance%}->get{%modelName%}($where);
return json($res);
}
/**
* 保存更新的资源
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function update{%actionSuffix%}({%modelName%} ${%modelInstance%}): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
try {
validate({%modelName%}Validate::class)->scene('update')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
$where = [
'id' => $param['id'],
'seller_id' => $this->admin['seller_id'],
'is_del' => 1,
];
$res = ${%modelInstance%} -> update{%modelName%}($where,$param);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 删除指定资源
*
* @throws \app\exception\ModelException
*/
public function delete{%actionSuffix%}({%modelName%} ${%modelInstance%}): \think\response\Json
{
if(request()->isPost()){
$id = (int)input('id');
if(!$id){
// TO DO
// 替换错误提示
return jsonReturn(-1,'ErrorMsg');
}
$where = [
'id' => $id,
'seller_id' => $this->admin['seller_id']
];
$res = ${%modelInstance%}->del{%modelName%}($where);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
}

View File

@ -0,0 +1,113 @@
<?php
declare (strict_types = 1);
namespace {%namespace%};
use app\exception\ModelException;
use app\exception\ModelEmptyException;
/**
* @mixin \think\Model
*/
class {%className%} extends Model
{
/**
* @param array $where
* @param int $limit
* @return array
* @throws ModelException
*/
public function get{%className%}List(array $where = [],int $limit = 10): array
{
try{
$res = $this->where($where)->paginate($limit);
}catch(\Exception $e){
throw new ModelException($e->getMessage());
}
return dataReturn($this->sucCode,$this->getMsg,$res);
}
/**
* @param array $where
* @return array
* @throws ModelEmptyException
* @throws ModelException
*/
public function get{%className%}(array $where = []): array
{
try{
$res = $this->where($where)->find();
if(empty($res)){
throw new ModelEmptyException();
}
}catch(ModelEmptyException $me){
throw new ModelEmptyException();
}catch(\Exception $e){
throw new ModelException($e->getMessage());
}
return dataReturn($this->sucCode,$this->getMsg,$res);
}
/**
* @param $param
* @return array
* @throws ModelException
*/
public function add{%className%}($param): array
{
try{
$res = self::create($param);
}catch(\Exception $e){
throw new ModelException($e->getMessage());
}
return dataReturn($this->sucCode,$this->addMsg,$res->id);
}
/**
* @param array $where
* @param array $param
* @return array
* @throws ModelException
*/
public function update{%className%}(array $where = [],array $param = []): array
{
try{
$res = self::where($where)->update($param);
}catch(\Exception $e){
throw new ModelException($e->getMessage());
}
return dataReturn($this->sucCode,$this->updateMsg,$res);
}
/**
* @param $where
* @return array
* @throws ModelException
*/
public function softDel{%className%}($where): array
{
try{
$res = $this->where($where)->update($this->delData);
}catch(\Exception $e){
throw new ModelException($e->getMessage());
}
return dataReturn($this->sucCode,$this->delMsg,$res);
}
/**
* @param $where
* @return array
* @throws ModelException
*/
public function del{%className%}($where): array
{
try{
$res = $this->where($where)->delete();
}catch(\Exception $e){
throw new ModelException($e->getMessage());
}
return dataReturn($this->sucCode,$this->delMsg,$res);
}
}

1241
app/common.php Normal file

File diff suppressed because it is too large Load Diff

1
app/controller/api/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
./*

View File

@ -0,0 +1,191 @@
<?php
declare (strict_types = 1);
namespace app\controller\backend;
use app\model\AdminMenu;
use app\model\PhoneCode;
use app\service\PhoneCodeService;
use app\service\RoleService;
use app\service\sms\ALiSms;
use app\validate\LoginValidate;
use app\model\Admin;
use think\exception\ValidateException;
use think\facade\Cache;
use think\facade\Env;
use think\facade\Lang;
use xiaodi\JWTAuth\Facade\Jwt;
class AccountController extends \app\BaseController
{
/**
* @param Admin $adminModel
* @return \think\response\Json
* @throws \app\exception\ModelEmptyException
* @throws \app\exception\ModelException
*/
public function login(Admin $adminModel): \think\response\Json
{
if (request()->isPost()) {
$param = request()->only(['account','password']);
$pattern = "/^[\w\.-]+@\w+\.\w+(\.\w+)?$/";
if(!env('APP_DEBUG') == true || !(preg_match($pattern,$param['account']) && env('APP_DEBUG') == true)){
$param['account'] = $this->RSA_openssl($param['account'], 'decode');
$param['password'] = $this->RSA_openssl($param['password'], 'decode');
}
// 检验完整性
try {
validate(LoginValidate::class)->scene('login')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
$where = ['account' => $param['account']];
$admin = $adminModel->getAdmin($where,['role'])['data'];
if (!checkPassword($param['password'], $admin['password'])) {
return jsonReturn(-2, Lang::get('用户名密码不正确'));
}
if ($admin['status'] == 2) {
return jsonReturn(-3, Lang::get('该账号已经被禁用'));
}
if ($admin['code_auth'] == 1 && !empty($admin['phone'])) {
$data = [
'phone' => $admin['phone'],
'code' => random_code_type(6, 'number'),
'seller_id' => $admin['seller_id'],
'expired_time' => date('Y-m-d H:i:s', time() + 5 * 60), // 登录验证码5分钟有效
];
//保存发送code记录
$codeModel = new PhoneCode();
$codeModel->insertGetId([
'phone' => $data['phone'],
'code' => $data['code'],
'expired_time' => $data['expired_time']
]);
Cache::set($admin['phone'] . '_login', time(), 5 * 60);
if (Env::get('APP_DEBUG')) {
return jsonReturn(0, 'code_auth', $data);
}
$res = ALiSms::sendOneAuthMsg($data);
if ($res['code'] != 0) {
return json($res);
}
return jsonReturn(0, 'code_auth', ['phone' => $admin['phone']]);
}
return $this->getMenuAndToken($admin);
}
}
/**
* @throws \app\exception\ModelEmptyException
* @throws \app\exception\ModelException
*/
public function authCode(Admin $adminModel): \think\response\Json
{
$param = request()->only(['phone','code']);
$param['phone'] = $this->RSA_openssl($param['phone'], 'decode');
$param['code'] = $this->RSA_openssl($param['code'], 'decode');
$authCodeRes = PhoneCodeService::authCode($param);
if ($authCodeRes['code'] != 0) {
return json($authCodeRes);
}
$where = ['phone' => $param['phone']];
$admin = $adminModel->getAdmin($where,['role'])['data'];
return $this->getMenuAndToken($admin);
}
/**
* @throws \app\exception\TokenException
*/
protected function getMenuAndToken($admin): \think\response\Json
{
// 缓存权限数据
$roleRes = RoleService::getMenuAndUpdateAuth($admin['id']);
// $token = Jwt::token([
// 'uid' => $admin['id'],
// 'name' => $admin['name'],
// 'seller_id' => $admin['seller_id']
// ]);
$loginTime = time();
// 登录日志
$data = [
'seller_id' => $admin['seller_id'],
'admin_id' => $admin['id'],
'admin_name' => $admin['name'],
'title' => $admin['name'].Lang('登录后台系统'),
'ip' => request()->ip(),
'agent' => request()->header()['user-agent'],
'login_time' => $loginTime
];
event('AdminLoginLog',$data);
// 更新最后登录时间
$admin->last_login_time = $loginTime;
$admin->save();
$menu = $roleRes['data'];
return jsonReturn(0, Lang::get('登录成功'), [
'token' => createUserToken([
'uid' => $admin['id'],
'name' => $admin['name'],
'seller_id' => $admin['seller_id']
],'admin'),
'user' => $admin,
'menu' => $menu,
]);
}
/**
* @description RSA公钥加密 私钥解密
* @param string $data 待加解密数据
* @param string $operate 操作类型 encode:加密 decode:解密
* @return string 返回加密内容/解密内容
* @throws \Exception
*/
protected function RSA_openssl(string $data, string $operate = 'encode'): string
{
//RSA 公钥
$rsa_public = config('system.rsa_public_key');
//RSA 私钥
$rsa_private = config('system.rsa_private_key');
//RSA 公钥加密
if ('encode' == $operate) {
$public_key = "-----BEGIN PUBLIC KEY-----\n" . wordwrap($rsa_public, 64, "\n", true) . "\n-----END PUBLIC KEY-----";
$key = openssl_pkey_get_public($public_key);
if (!$key) {
throw new \Exception('公钥不可用,请联系系统维护人员');
}
$return_en = openssl_public_encrypt($data, $crypted, $key);
if (!$return_en) {
throw new \Exception('公钥加密失败,请联系系统维护人员');
}
return base64_encode($crypted);
}
//RSA 私钥解密
if ('decode' == $operate) {
$private_key = "-----BEGIN PRIVATE KEY-----\n" . wordwrap($rsa_private, 64, "\n", true) . "\n-----END PRIVATE KEY-----";
$key = openssl_pkey_get_private($private_key);
if (!$key) {
throw new \Exception('私钥不可用,请联系系统维护人员');
}
$return_de = openssl_private_decrypt(base64_decode($data), $decrypted, $key);
if (!$return_de) {
throw new \Exception('私钥解密失败,请联系系统维护人员');
}
return $decrypted;
}
}
}

View File

@ -0,0 +1,232 @@
<?php
declare (strict_types = 1);
namespace app\controller\backend;
use app\model\Admin;
use app\model\AdminMenu;
use app\validate\AdminValidate;
use think\exception\ValidateException;
use think\facade\Db;
use think\facade\Lang;
class AdminController extends BaseController
{
/**
* @param Admin $admin
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function index(Admin $admin): \think\response\Json
{
$where = [
'seller_id' => $this->admin['seller_id'],
'is_del' => 1,
];
$limit = $this->setLimit();
$adminList = $admin->getAdminList($where,$limit);
return json(pageReturn($adminList));
}
/**
* @throws \app\exception\ModelEmptyException
* @throws \app\exception\ModelException
*/
public function read(Admin $Admin): \think\response\Json
{
$id = (int)input('id');
if(!$id){
return jsonReturn(-1,Lang::get('管理员ID不能为空'));
}
$where = [
'id' => $id,
'seller_id' => $this->admin['seller_id']
];
$res = $Admin->getAdmin($where,['role']);
return json($res);
}
/**
* 新建管理员
*
* @param Admin $admin
* @return \think\Response
* @throws \app\exception\ModelException
*/
public function save(Admin $admin): \think\Response
{
$param = input('post.');
try{
validate(AdminValidate::class)->scene('save')->check($param);
}catch(ValidateException $e){
return jsonReturn(-1, $e->getError());
}
$param['password'] = makePassword($param['password']);
if(isset($param['group'])){
unset($param);
}
Db::startTrans();
try{
$res = $admin -> addAdmin($param);
$res['data']->role()->attach($param['role'],['seller_id'=>$this->admin['seller_id']]);
event("AdminOptLog",[
'admin_id' => $this->admin['uid'],
'admin_name' => $this->admin['name'],
'title'=>Lang::get('管理员管理'),
"content"=>Lang::get('新增管理员').$res['data']['id'],
]);
Db::commit();
}catch (\Exception $e){
Db::rollback();
return jsonReturn(-3,$e->getMessage());
}
return json($res);
}
/**
* 编辑管理员
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function update(Admin $admin): \think\response\Json
{
if(request()->isPost()){
$param = request()->except(['password'], 'post');
$password = request()->post('password');
try {
validate(AdminValidate::class)->scene('update')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
$where = [
'id' => $param['id'],
'seller_id' => $this->admin['seller_id'],
'is_del' => 1,
];
Db::startTrans();
try{
if(isset($param['group']) && $param['group'] == 1){
return jsonReturn(-1,Lang::get('系统管理员不能修改'));
}
$manager = $admin -> getAdmin($where)['data'];
$manager->role()->detach();
$manager->role()->attach($param['role'],['seller_id'=>$this->admin['seller_id']]);
if(!empty($password)){
$param['password'] = makePassword($password);
}
unset($param['role']);
$res = $admin->updateAdmin($where,$param);
event("AdminOptLog",[
'admin_id' => $this->admin['uid'],
'admin_name' => $this->admin['name'],
'title'=>Lang::get('管理员管理'),
"content"=>Lang::get('更新管理员信息').$param['id'],
]);
Db::commit();
}catch (\Exception $e){
Db::rollback();
return jsonReturn(-3,$e->getMessage());
}
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 删除管理员
*
* @throws \app\exception\ModelException
*/
public function delete(Admin $admin): \think\response\Json
{
if(request()->isPost()){
$id = (int)input('id');
if(!$id){
return jsonReturn(-1,Lang::get('管理员ID不能为空'));
}
$where = [
'id' => $id,
'seller_id' => $this->admin['seller_id']
];
event("AdminOptLog",[
'admin_id' => $this->admin['uid'],
'admin_name' => $this->admin['name'],
'title'=>Lang::get('管理员管理'),
"content"=>Lang::get('删除管理员').$id,
]);
$res = $admin->delAdmin($where);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 修改个人资料
*
* @throws \app\exception\ModelException
*/
public function selfUpdate(Admin $admin): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
try {
validate(AdminValidate::class)->scene('selfUpdate')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
if ($param['code_auth'] == 1 && empty($param['phone'])) {
return jsonReturn(-2, Lang::get('开启短信验证登录需填写手机号'));
}
$where = [
'id' => $this->admin['uid'],
'seller_id' => $this->admin['seller_id'],
];
if (empty($param['password'])) {
unset($param['password']);
} else {
$param['password'] = makePassword($param['password']);
}
$res = $admin -> updateAdmin($where,$param);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* @throws \app\exception\ModelException
* @throws \app\exception\ModelEmptyException
*/
public function self(Admin $Admin,AdminMenu $AdminMenu): \think\response\Json
{
$admin = $Admin->getAdmin(['id' => $this->admin['uid'],'seller_id' => $this->admin['seller_id']],['role'])['data'];
// 获取权限
$role = $admin['role']->toArray();
if(empty($role)){
$auth = [];
}else{
$roleIds = array_column($role,'id');
if(in_array(1,$roleIds)){
$auth = $AdminMenu->getAllAdminMenu(['is_menu'=> 1,'seller_id'=>1])['data']->toArray();
}else{
$menuIds = array_column($role,'auth');
$tmpMenuIds = [];
foreach ($menuIds as $menuId){
$tmp = explode(',',$menuId);
$tmpMenuIds = array_merge($tmpMenuIds,$tmp);
}
$whereIn = array_unique($tmpMenuIds);
$auth = $AdminMenu -> getAllAdminMenu([['id','in',$whereIn],['is_menu','=',1]])['data']->toArray();
}
}
$menu = makeTree($auth);
return jsonReturn(0,'success',[
'user' => $admin,
'menu' => $menu,
]);
}
}

View File

@ -0,0 +1,40 @@
<?php
declare (strict_types = 1);
namespace app\controller\backend;
use app\model\AdminLoginLog;
use think\facade\Lang;
use think\facade\Request;
class AdminLoginLogController extends BaseController
{
/**
* 显示资源列表
*
* @return \think\Response
* @throws \app\exception\ModelException
*/
public function index(AdminLoginLog $adminLoginLog): \think\Response
{
$where = [
'seller_id' => $this->admin['seller_id'],
];
$limit = $this->setLimit();
$name = input('admin_name');
if($name){
$where['admin_name'] = $name;
}
$id = input('admin_id');
if($id){
$where['id'] = $id;
}
$adminLogList = $adminLoginLog->getAdminLoginLogList($where,$limit);
return json(pageReturn($adminLogList));
}
public function delete(): \think\response\Json
{
return parent::customDelete(Lang::get('登录日志'),Lang::get('登录日志删除'));
}
}

View File

@ -0,0 +1,171 @@
<?php
declare (strict_types = 1);
namespace app\controller\backend;
use app\model\AdminMenu;
use app\service\RoleService;
use app\validate\AdminMenuValidate;
use think\exception\ValidateException;
use think\facade\Lang;
class AdminMenuController extends BaseController
{
/**
* 显示资源列表
*
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function index(AdminMenu $adminMenu): \think\response\Json
{
$where = [
'seller_id' => $this->admin['seller_id'],
'is_del' => 1,
];
$adminMenuList = $adminMenu->getAllAdminMenu($where)['data']->toArray();
// foreach($adminMenuList as $key => $val){
// if($val['is_menu'] == 2){
// unset($adminMenuList[$key]);
// }
// }
$adminMenuList = generate($adminMenuList);
return jsonReturn(0,'success',$adminMenuList);
}
/**
* 保存新建的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function save(AdminMenu $adminMenu): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
// 数据验证
try{
validate(AdminMenuValidate::class)->scene('save')->check($param);
}catch(ValidateException $e){
return jsonReturn(-1, $e->getError());
}
$param['path'] = '/' .$param['controller'].'/'.$param['action'];
$res = $adminMenu -> addAdminMenu($param);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 显示指定的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \app\exception\ModelEmptyException
*/
public function read(AdminMenu $adminMenu): \think\response\Json
{
$id = (int)input('id');
if(!$id){
// TODO
// 修改错误消息
return jsonReturn(-1,'ErrorMsg');
}
$where = [
'id' => $id,
'seller_id' => $this->admin['seller_id']
];
// TODO
// 其他逻辑
$res = $adminMenu->getAdminMenu($where);
return json($res);
}
/**
* 保存更新的资源
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function update(AdminMenu $adminMenu): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
try {
validate(AdminMenuValidate::class)->scene('update')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
$param['path'] = '/' .$param['controller'].'/'.$param['action'];
$where = [
'id' => $param['id'],
'seller_id' => $this->admin['seller_id'],
'is_del' => 1,
];
$res = $adminMenu -> updateAdminMenu($where,$param);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 删除指定资源
*
* @throws \app\exception\ModelException
*/
public function delete(AdminMenu $adminMenu): \think\response\Json
{
if(request()->isPost()){
$id = (int)input('id');
if(!$id){
return jsonReturn(-3,Lang::get('请求方法错误'));
}
$where = [
'id' => $id,
'seller_id' => $this->admin['seller_id']
];
$res = $adminMenu->softDelAdminMenu($where);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 单个控制菜单是否显示
* @author:🍁
* @Time:2023/5/22 11:12 上午
*/
public function updateMenuStatus(AdminMenu $adminMenu)
{
$id = $this->request->post('id/d',0);
$info = $adminMenu->where(['id'=>$id,'is_del'=>1])->field('id,status')->find();
if(empty($info)){
return jsonReturn(-1,lang('菜单不存在'));
}
$updateData = [
'status'=>$info['status']==1?2:1,
'update_time'=>date("Y-m-d H:i:s")
];
$res = $adminMenu->where(['id'=>$id])->update($updateData);
if($res){
return jsonReturn(0,lang('修改成功'),$updateData);
}
return jsonReturn(-2,lang('修改失败'));
}
/**
* 得到当前登录用户的权限节点
* @author:🍁
* @Time:2023/5/22 11:24 上午
*/
public function getUserMenuList()
{
return json(RoleService::getMenuAndUpdateAuth($this->admin['uid']));
}
}

View File

@ -0,0 +1,41 @@
<?php
declare (strict_types = 1);
namespace app\controller\backend;
use app\model\AdminOptLog;
use think\facade\Lang;
class AdminOptLogController extends BaseController
{
/**
* 显示资源列表
*
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function index(AdminOptLog $adminOptLog): \think\response\Json
{
$where = [
'seller_id' => $this->admin['seller_id'],
];
$limit = $this->setLimit();
$name = input('admin_name');
if($name){
$where['admin_name'] = $name;
}
$id = input('admin_id');
if($id){
$where['id'] = $id;
}
$adminLogList = $adminOptLog->getAdminOptLogList($where,$limit);
return json(pageReturn($adminLogList));
}
public function delete(): \think\response\Json
{
return parent::customDelete(Lang::get('操作日志'),Lang::get('操作日志删除'));
}
}

View File

@ -0,0 +1,140 @@
<?php
declare (strict_types = 1);
namespace app\controller\backend;
use app\model\Advertisement;
use app\model\Attachment;
use app\validate\AdvertisementValidate;
use think\exception\ValidateException;
use think\facade\Lang;
class AdvertisementController extends BaseController
{
/**
* 显示资源列表
*
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function index(Advertisement $advertisement): \think\response\Json
{
$where = [
'seller_id' => $this->admin['seller_id'],
'is_del' => 1,
];
$title = input('title');
if ($title) {
$where = [ 'title' => $title];
}
$limit = $this->setLimit();
$with = [
'attachment' => function($query){
$query->field('id,name,type,url');
}
];
$advertisementList = $advertisement->getAdvertisementList($where,$limit,'*',$with);
return json(pageReturn($advertisementList));
}
/**
* 保存新建的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function save(Advertisement $advertisement): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
$param['seller_id'] = $this->admin['seller_id'];
// 数据验证
try{
validate(AdvertisementValidate::class)->scene('save')->check($param);
}catch(ValidateException $e){
return jsonReturn(-1, $e->getError());
}
$param['expiration_date'] = date('Y-m-d',strtotime($param['expiration_date']));
$param['status'] = 2; // 默认状态禁用
$res = $advertisement -> addAdvertisement($param);
optEventLog($res['data']['id'],Lang::get('广告'),Lang::get('新增'));
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 显示指定的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \app\exception\ModelEmptyException
*/
public function read(Advertisement $advertisement): \think\response\Json
{
$id = (int)input('id');
if(!$id){
return jsonReturn(-1,Lang::get('广告ID不能为空'));
}
$where = [
'id' => $id,
'seller_id' => $this->admin['seller_id']
];
$res = $advertisement->getAdvertisement($where,'',['attachment'=>function($q){
$q->field('id,name,type,url');
}]);
return json($res);
}
/**
* 保存更新的资源
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function update(Advertisement $advertisement): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
try {
validate(AdvertisementValidate::class)->scene('update')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
$where = [
'id' => $param['id'],
'seller_id' => $this->admin['seller_id'],
];
$param['expiration_date'] = date('Y-m-d',strtotime($param['expiration_date']));
$res = $advertisement -> updateAdvertisement($where,$param);
optEventLog($param['id'],Lang::get('广告'),Lang::get('更新'));
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 删除指定资源
*
* @throws \app\exception\ModelException
*/
public function delete(Advertisement $advertisement): \think\response\Json
{
if(request()->isPost()){
$id = (int)input('id');
if(!$id){
return jsonReturn(-1,'ErrorMsg');
}
$where = [
'id' => $id,
'seller_id' => $this->admin['seller_id']
];
optEventLog($id,Lang::get('广告'),Lang::get('删除'));
$res = $advertisement->softDelAdvertisement($where);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
}

View File

@ -0,0 +1,156 @@
<?php
declare (strict_types = 1);
namespace app\controller\backend;
use app\model\AttachmentCate;
use app\service\CacheService;
use app\validate\AttachmentCateValidate;
use think\exception\ValidateException;
use think\facade\Lang;
class AttachmentCateController extends BaseController
{
/**
* 显示资源列表
*
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function index(AttachmentCate $attachmentCate): \think\response\Json
{
$where = [
['seller_id' ,'=', $this->admin['seller_id']],
];
$title = input('title');
if(!empty($title)){
$where[] = ['title', 'like', '%' . $title . '%'];
}
$attachmentCateList = $attachmentCate->getAllCustomArrayData($where)['data'];
$attachmentCateList = makeTree($attachmentCateList);
array_unshift($attachmentCateList,[
'id' => 0,
'parent_id' => 0,
'path' => 0,
'title'=> Lang::get('全部分类'),
'children' => [],
]);
return jsonReturn(0,Lang::get('成功'),$attachmentCateList);
}
/**
* 保存新建的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \app\exception\ModelNotUniqueException
* @throws \ReflectionException
*/
public function save(AttachmentCate $attachmentCate): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
// 数据验证
try{
validate(AttachmentCateValidate::class)->scene('save')->check($param);
}catch(ValidateException $e){
return jsonReturn(-1, $e->getError());
}
$param['seller_id'] = $this->admin['seller_id'];
$attachmentCate->saveUnique(['title'=>$param['title'],'seller_id'=>$param['seller_id'],'parent_id'=>$param['parent_id']],'栏目已经存在');
// 完善栏目数据
if ($param['parent_id']) {
$parentCate = $attachmentCate->getCustomData(['id' => $param['parent_id'], 'seller_id' => $param['seller_id']])['data'];
$param['path'] = $parentCate['path'] . '-' . $param['parent_id'];
} else {
$param['path'] = 0;
}
$res = $attachmentCate -> addAttachmentCate($param);
CacheService::deleteRelationCacheByObject($attachmentCate);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 显示指定的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \app\exception\ModelEmptyException
*/
public function read(AttachmentCate $attachmentCate): \think\response\Json
{
$id = (int)input('id');
if(!$id){
return jsonReturn(-1,Lang::get('分类ID不能为空'));
}
$where = [
'id' => $id,
];
// 其他逻辑
$res = $attachmentCate->getAttachmentCate($where);
return json($res);
}
/**
* 保存更新的资源
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \ReflectionException
*/
public function update(AttachmentCate $attachmentCate): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
try {
validate(AttachmentCateValidate::class)->scene('update')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
$where = [
'id' => $param['id'],
'seller_id' => $this->admin['seller_id'],
];
$res = $attachmentCate -> updateAttachmentCate($where,$param);
CacheService::deleteRelationCacheByObject($attachmentCate);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 删除指定资源
*
* @throws \app\exception\ModelException
* @throws \ReflectionException
*/
public function delete(AttachmentCate $attachmentCate): \think\response\Json
{
if(request()->isPost()){
$id = (int)input('id');
if(!$id){
return jsonReturn(-1,lang('分类ID不能为空'));
}
$where = [
'id' => $id,
'seller_id' => $this->admin['seller_id']
];
$subCate = $attachmentCate -> getAllCustomArrayData(['seller_id'=>$this->admin['seller_id'],'parent_id'=>$id])['data'];
if(!empty($subCate)){
return jsonReturn(-1,Lang::get('分类下面有子分类,不能直接删除'));
}
$cate = $attachmentCate -> getCustomData($where,'attachment')['data']->toArray();
if(!empty($cate['attachment'])){
return jsonReturn(-2,Lang::get('分类下有附件不能直接删除'));
}
$res = $attachmentCate->delAttachmentCate($where);
CacheService::deleteRelationCacheByObject($attachmentCate);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
}

View File

@ -0,0 +1,462 @@
<?php
declare (strict_types = 1);
namespace app\controller\backend;
use app\exception\ModelException;
use app\model\Attachment;
use app\model\AttachmentCate;
use app\model\Model;
use app\model\RecycleBin;
use app\model\SysSetting;
use app\service\CacheService;
use app\service\ThemeService;
use app\service\upload\Upload;
use app\validate\AttachmentValidate;
use think\exception\ValidateException;
use think\facade\Db;
use think\facade\Lang;
use think\file\UploadedFile;
class AttachmentController extends BaseController
{
protected $type = [
'file' => 4,
'image' => 2,
'video' => 3,
'mp3' => 5,
'zip' => 1,
];
/**
* 显示资源列表
*
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function index(Attachment $attachment): \think\response\Json
{
$type = (int)input('type');
$where = [
['seller_id' ,'=', $this->admin['seller_id']],
['is_del' ,'=', 1],
];
$cate_id = (int)input('cate_id');
if($cate_id){
$Cate = new AttachmentCate();
$cate = $Cate -> getCustomData(['id'=>$cate_id,'seller_id'=>$this->admin['seller_id']])['data'];
$path = $cate['path'] . '-' . $cate['id'];
$cates = $Cate -> getAllCustomArrayData([['path','like',$path.'%'],['seller_id','=',$this->admin['seller_id']]])['data'];
$ids = array_column($cates,'id');
array_unshift($ids,$cate['id']);
if(count($ids) == 1){
$where[] = ['attachment_cate_id', '=', $ids[0]];
}else{
$where[] = ['attachment_cate_id', 'in', $ids];
}
}
if($type){
$where[] = ['type', '=', $type];
}
$name = input('name');
if(!empty($name)){
$where[] = ['name', 'like', '%' . $name . '%'];
}
$limit = $this->setLimit();
$attachmentList = $attachment->getAttachmentList($where,$limit);
$res = pageReturn($attachmentList);
foreach ($res['data'] as &$value) {
$value['prefix_url'] = '';
$value['suffix_url'] = '';
if (strpos($value['url'], 'http') === false) {
if (strpos($value['url'], 'storage/') === false) {
$value['prefix_url'] = 'http://' . $this->request->host() . 'storage/';
$value['suffix_url'] = $value['url'];
$value['url'] = 'http://' . $this->request->host() . $value['url'];
continue;
}
$value['url'] = 'http://' . $this->request->host() . $value['url'];
//前缀,后缀
$urlStr = explode('storage/', $value['url']);
$value['prefix_url'] = $urlStr[0] . 'storage/';
$value['suffix_url'] = $urlStr[1];
}
}
return json($res);
}
// 修改附件路径
public function editFileUrl()
{
$param = $this->request->param();
if (empty($param['id']) || empty($param['name']) || empty($param['suffix_url'])) {
return json(dataReturn(-1, Lang::get('必填项不能为空!')));
}
$attachmentModel = new Attachment();
$info = $attachmentModel->where('id', $param['id'])->find();
if (!isset($info['id'])) {
return json(dataReturn(-1, Lang::get('文件不存在!')));
}
$param['suffix_url'] = str_replace('..', '', $param['suffix_url']);
$param['suffix_url'] = str_replace('//', '/', $param['suffix_url']);
$oldUrl = $info['url'];
$oldPathUrl = CMS_ROOT . 'public' . $oldUrl;
$newUrl = 'storage/' . $param['suffix_url'];
$newPathUrl = CMS_ROOT . 'public/' . $newUrl;
$updateData = [
'name' => $param['name'],
'description' => $param['description'],
'url' => $newUrl,
];
$fileInfo = pathinfo($newPathUrl);
mkdirs($fileInfo['dirname']);
$copyRes = true;
if ($oldPathUrl != $newPathUrl) {
$copyRes = copy($oldPathUrl, $newPathUrl);
}
if ($copyRes) {
// 复制成功
$res = $attachmentModel->where('id', $param['id'])->update($updateData);
} else {
$res = false;
}
if ($res === false) {
return json(dataReturn(-1, lang('修改失败')));
} else {
return json(dataReturn(0, lang('修改成功')));
}
}
/**
* 保存新建的资源
*
* @return \think\response\Json
* @throws \ReflectionException
*/
public function save(Attachment $attachment): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
foreach($param as &$val){
try{
validate(AttachmentValidate::class)->scene('save')->check($val);
$val['seller_id'] = $this->admin['seller_id'];
}catch (ValidateException $e){
return jsonReturn(-1, $e->getError());
}
if ($val['storage'] == 5 && strpos($val['url'], 'http') !== 0) {
return jsonReturn(-1, lang('网络附件链接地址请以http开头'));
}
}
$res = $attachment->addAllCustomData($param);
CacheService::deleteRelationCacheByObject($attachment);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 显示指定的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function read(Attachment $attachment): \think\response\Json
{
$id = (int)input('id');
if(!$id){
return jsonReturn(-1,lang('附件ID不能为空'));
}
$where = [
'id' => $id,
'seller_id' => $this->admin['seller_id'],
'is_del' => 1,
];
$res = $attachment->getAttachment($where);
return json($res);
}
/**
* 保存更新的资源
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \ReflectionException
*/
public function update(Attachment $attachment): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
$seller_id = $this->admin['seller_id'];
try {
validate(AttachmentValidate::class)->scene('update')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
if (isset($param['storage']) && $param['storage'] == 5 && strpos($param['url'], 'http') !== 0) {
return jsonReturn(-1, lang('网络附件链接地址请以http开头'));
}
$where = [
'id' => $param['id'],
'seller_id' => $seller_id,
];
$res = $attachment -> updateAttachment($where,$param);
CacheService::deleteRelationCacheByObject($attachment);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 删除指定资源
*
* @throws \app\exception\ModelException
*/
public function delete(Attachment $Attachment): \think\response\Json
{
$idArr = $this->request->param('id');
if(is_string($idArr)){
$ids = explode(',',$idArr);
}else{
$ids = $idArr;
}
$where = [
['id','in',$ids],
['seller_id','=',$this->admin['seller_id']]
];
$attachment = $Attachment -> getAllCustomArrayData($where)['data'];
Db::startTrans();
try{
$binData=[];
foreach ($attachment as $val){
// 复制删除内容到回收站表
$binData[] = [
'seller_id' => $this->admin['seller_id'],
'object_id' => $val['id'],
'sub_id' => 0,
'module_id' => 0,
'table_name' => '附件-attachment',
'title' => !empty($val['name']) ? $val['name'] : '附件' . $val['id'],
'admin_id' => $this->admin['uid'],
'name' => $this->admin['name'],
];
}
$recycleBin = new RecycleBin();
$recycleBin -> addAllCustomData($binData);
$Attachment -> updateAttachment($where,['is_del'=>2,'delete_time'=>time()]);
CacheService::deleteRelationCacheByObject($Attachment);
optEventLog($idArr,Lang::get('附件'),Lang::get('删除'));
Db::commit();
}catch (\Exception $e){
Db::rollback();
return jsonReturn(-1,Lang::get('删除失败'));
}
return jsonReturn(0,Lang::get('删除成功'));
}
/**
* @throws \app\exception\ModelException
*/
public function uploadAndSave(Attachment $attachment): \think\response\Json
{
if(request()->isPost()){
set_time_limit(0);
$file = request()->file('file');
$reduceImg = request()->file('reduce_img');
if(empty($file)){
return jsonReturn(-8,lang('文件上传失败,请重新尝试'));
}
$seller_id = $this->admin['seller_id'];
$attachmentModel = new Attachment();
$has = $attachmentModel->checkFileExist($this->admin['seller_id'], $file->hash());
if ($has['code'] == 0) {
$reduceImgData = !empty($reduceImg)?$this->singleUpload($reduceImg,$seller_id)['data']:[];
$updateData = [
'update_time' => time(),
'reduce_img'=> !empty($reduceImgData)?$reduceImgData['getUploadFileInfo']['url']:"",
];
// 更新時間
$attachmentModel->where('id', $has['data']['id'])->update($updateData);
return json($has);
}
$fileData = $this->singleUpload($file,$seller_id)['data'];
$reduceImgData = !empty($reduceImg)?$this->singleUpload($reduceImg,$seller_id)['data']:[];
$fileData['getUploadFileInfo']['seller_id'] = $seller_id;
$fileData['getUploadFileInfo']['type'] = $fileData['fileType'];
$fileData['getUploadFileInfo']['size'] = round($fileData['fileSize'] / 1024,2);
$fileData['getUploadFileInfo']['attachment_cate_id'] =$this->request->param('attachment_cate_id/d',0);
$fileData['getUploadFileInfo']['reduce_img'] = !empty($reduceImgData)?$reduceImgData['getUploadFileInfo']['url']:"";
$attachment = new Attachment();
$res = $attachment->addAttachment($fileData['getUploadFileInfo']);
optEventLog($res['data']['id'],Lang::get('附件'),Lang::get('添加'));
return json($res);
}
return jsonReturn(-3,lang('请求方法错误'));
}
/**
* @throws ModelException
*/
private function singleUpload($file,$seller_id)
{
// 查看文件类型
$fileName = $file->getOriginalName();
$fileExt = $file->getOriginalExtension();
$file_type = fileFormat($fileName);
$fileType = $this->type[$file_type];
$Settings = new SysSetting();
$uploadSetting = $Settings->getAllCustomArrayData(['parent_id'=>1,'group'=>'upload','status'=>1],'id desc','id,group,title,value')['data'];
$uploadSetting = getColumnForKeyArray($uploadSetting,'title');
$limitSize = $uploadSetting[$file_type.'_size']['value'] * 1024; // byte
$fileSize = $file->getSize(); // 单位byte
if($fileSize > $limitSize){
throw new ModelException(lang('文件过大,请修改上传限制或者替换小的文件'),-1);
}
$extArr = explode(',',$uploadSetting[$file_type.'_ext']['value']);
if(!in_array($fileExt,$extArr)){
throw new ModelException(lang('文件格式错误,请重新上传'),-2);
}
$type = $this->getUploadType();
$upload = new Upload();
$info = $upload->create($file,$seller_id, $type,$file_type);
if(!$info){
throw new ModelException(lang('上传失败请重新尝试'),-3);
}
return dataReturn(0,'上传成功',
[
'fileType'=>$fileType,
'fileSize'=>$fileSize,
'getUploadFileInfo'=>$upload->getUploadFileInfo()['data']
]);
}
public function sliceUploadAndSave(Upload $uploader): \think\response\Json
{
$params = $this->request->param();
$params['resource_chunk'] = $this->request->file('resource_chunk');
$file_type = fileFormat('abc.'.$params['resource_type']);
$Settings = new SysSetting();
$uploadSetting = $Settings->getAllCustomArrayData(['parent_id'=>1,'group'=>'upload','status'=>1],'id desc','id,group,title,value')['data'];
$uploadSetting = getColumnForKeyArray($uploadSetting,'title');
$extArr = explode(',',$uploadSetting[$file_type.'_ext']['value']);
if(!in_array($params['resource_type'],$extArr)){
return jsonReturn(-2,lang('文件格式错误,请重新上传'));
}
$res = $uploader->tmpMove($params['resource_chunk'],$file_type,$params['resource_temp_path'],$params['chunk_index']);
if($res){
$tmPath = runtime_path() . 'slice/'.$file_type.'/'.$params['resource_temp_path'];
$num = getFileNumber($tmPath);
if($num == $params['chunk_total']){
try {
$filepath = $uploader->mergeBlob($tmPath,$params['resource_temp_path'],$params['chunk_total'],$params['resource_type']);
$type = $this->getUploadType();
$file = new UploadedFile($filepath,$params['resource_temp_path'].'.'.$params['resource_type']);
if($type == 'local'){
if(!file_exists(public_path() . 'storage/' . $file_type.'/'. date('Ymd'))){
@mkdir(public_path() . 'storage/' . $file_type.'/'. date('Ymd'),0755,true);
}
copy($filepath,public_path() . 'storage/' . $file_type.'/'. date('Ymd') .'/'.$params['resource_temp_path'] .'.'.$params['resource_type']);
$cateId = (int)input('attachment_cate_id');
$uploadInfo['name'] = $params['resource_name'];
$uploadInfo['url'] = 'storage/' . $file_type.'/'. date('Ymd') .'/'.$params['resource_temp_path'] .'.'.$params['resource_type'];
$uploadInfo['mime_type'] = $params['resource_type'];
$uploadInfo['storage'] = 1;
$uploadInfo['type'] = $this->type[$file_type];
$uploadInfo['size'] = round($file->getSize() / 1024,2);
$uploadInfo['attachment_cate_id'] = $cateId;
$attachment = new Attachment();
$res = $attachment->addAttachment($uploadInfo);
optEventLog($res['data']['id'],Lang::get('附件'),Lang::get('添加'));
(new ThemeService()) -> deleteFile($tmPath);
return json($res);
}else{
$upload = new Upload();
$res = $upload->create($file,$this->admin['seller_id'], $type);
if($res){
$fileSize = $file->getSize();
$fileType = $this->type[$file_type];
$res = $this->getResponse($fileType,$fileSize,$upload);
(new ThemeService()) -> deleteFile($tmPath);
return json($res);
}else{
return jsonReturn(-1,Lang::get('上传失败'));
}
}
} catch (\Exception $e) {
return jsonReturn(-1,$e->getMessage(),[]);
}
}else{
return jsonReturn(1,Lang::get('上传成功'),$res);
}
}else{
return jsonReturn(-1,Lang::get('上传失败'),$res);
}
}
/**
* @throws \app\exception\ModelException
*/
public function getResponse($fileType, $fileSize, $upload)
{
$uploadInfo = $upload->getUploadFileInfo();
$cateId = (int)input('attachment_cate_id');
if ($uploadInfo['code'] == 0) {
$uploadInfo['data']['type'] = $fileType;
$uploadInfo['data']['size'] = round($fileSize / 1024,2);
$uploadInfo['data']['attachment_cate_id'] = $cateId;
$attachment = new Attachment();
$res = $attachment->addAttachment($uploadInfo['data']);
optEventLog($res['data']['id'],Lang::get('附件'),Lang::get('添加'));
return $res;
} else {
return $uploadInfo;
}
}
public function getFileType()
{
}
/**
* @throws \app\exception\ModelException
*/
public function getUploadType()
{
// 文件信息提取
$where = [
'seller_id' => $this->admin['seller_id'],
'group' => 'upload',
'title' => 'storage'
];
$place = new SysSetting();
return $place->getSysSetting($where)['data']->toArray()['value'];
}
}

View File

@ -0,0 +1,90 @@
<?php
declare (strict_types = 1);
namespace app\controller\backend;
use app\BaseController as AppBaseController;
use app\model\Model;
use think\facade\Lang;
use think\facade\Request;
class BaseController extends AppBaseController
{
public $admin;
public $lang;
/**
* 控制器中间件
* @var array
*/
protected $middleware = [
'login',
'auth',
];
public function initialize()
{
parent::initialize();
self::createInstallFile();
$this->admin = $this->request->admin;
session('adminId', $this->admin['uid']);
$this->lang = config('lang.allow_lang_list');
}
public function setLimit(): int
{
$limit = 10;
$limitPage = (int)input('param.limit');
if($limitPage){
$limit = $limitPage;
}
return $limit;
}
private static function createInstallFile()
{
if(!hcInstalled() && is_dir(app()->getRootPath().'data/install/')){
$url = env('app_host') . '/install';
redirect($url);
}
// 更新版本标识
updateVersion();
}
public function customDelete($opt,$title='批量删除'): \think\response\Json
{
$ids = $this->request->param('id');
if(is_string($ids)){
$ids = explode(',',$ids);
}
$className = str_replace('backend.','',Request::controller());
$modelName = '\\app\\model\\' . $className;
$model = new $modelName();
try{
$model->whereIn('id',$ids)->delete();
optEventLog(json_encode($ids),$title,$opt);
}catch (\Exception $e){
return jsonReturn(-1,Lang::get('删除失败'));
}
return jsonReturn(0,Lang::get('删除成功'));
}
public function customSoftDelete($opt,$title='批量删除'): \think\response\Json
{
$ids = $this->request->param('id');
if(is_string($ids)){
$ids = explode(',',$ids);
}
$className = str_replace('backend.','',Request::controller());
$modelName = '\\app\\model\\' . $className;
$model = new $modelName();
try{
$model->whereIn('id',$ids)->update(['is_del'=>2,'delete_time'=>time()]);
optEventLog(json_encode($ids),$title,$opt);
}catch (\Exception $e){
return jsonReturn(-1,Lang::get('删除失败'));
}
return jsonReturn(0,Lang::get('删除成功'));
}
}

View File

@ -0,0 +1,495 @@
<?php
declare (strict_types = 1);
namespace app\controller\backend;
use app\exception\ModelException;
use app\model\Attachment;
use app\model\Category;
use app\model\CategoryCheck;
use app\model\Route;
use app\model\ThemeFile;
use app\model\Website;
use app\service\ApiService;
use app\service\CacheService;
use app\service\CategoryService;
use app\service\StaticFileService;
use app\validate\CategoryValidate;
use Overtrue\Pinyin\Pinyin;
use think\exception\ValidateException;
use think\facade\Cache;
use think\facade\Db;
use think\facade\Lang;
class CategoryController extends BaseController
{
protected $cacheKeyArr = 'hc_module_cate_arr';
/**
* 栏目列表
*
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \app\exception\ModelEmptyException
*/
public function index(Category $category): \think\response\Json
{
$websiteId = (int)input('param.website_id');
if(!$websiteId){
return jsonReturn(-1,Lang::get('网站ID不能为空'));
}
$where = [
['seller_id','=', $this->admin['seller_id']],
['website_id','=',$websiteId],
];
$lang = input('lang');
if(empty($lang)){
$lang = config('lang.default_lang');
}
$Website = new Website();
$website = $Website -> getCustomArrayData(['id'=>$websiteId,'seller_id'=>$this->admin['seller_id']])['data'];
$domain = $website['domain'];
if($lang != config('lang.default_lang')){
$domain = $domain .'/'.$lang;
}
$title = $this->request->param('title','');
if(!empty($title)) {
$where[] = ['title', 'like', '%' . $title . '%'];
}
$where[] = ['lang','=',$lang];
$with = [
'thumbnail' => function($query){
$query->field('id,name,url');
}
];
//栏目管理搜索栏目名称
$categoryTitle = $this->request->param("category_title/s",'','trim,strip_tags');
if(!empty($categoryTitle)){
$categoryIdPath = $category->where([
['seller_id','=', $this->admin['seller_id']],
['website_id','=',$websiteId],
['lang','=',$lang],
['title','like',"%{$categoryTitle}%"],
])->column('path','id');
if(empty($categoryIdPath)){
return jsonReturn(0,'内容不存在');
}
$selectIds = [];
foreach ($categoryIdPath as $pathId => $paths){
$paths = explode('-',$paths);
for ($i=1;$i<count($paths)-1;$i++){
$selectIds[$paths[$i]] = $paths[$i];
}
$selectIds[$pathId] = $pathId;
}
$where[] = ['id','in',$selectIds];
}
$themeFileModel = new ThemeFile();
$designFileList = $themeFileModel->getAllCustomArrayData([['is_design', '=', 1]])['data'];
$newDesignFileList = [];
foreach ($designFileList as $value) {
$value['file_name'] = str_replace('.html', '', $value['file']);
$newDesignFileList[$value['file']] = $value;
$newDesignFileList[$value['file_name']] = $value;
}
$categoryList = $category->getAllCustomArrayData($where,'id asc','id,seller_id,parent_id,title,lang,alias,type,path,thumbnail,sort,status,list_tpl',$with)['data'];
foreach($categoryList as &$item){
if($item['type'] == 1 || $item['type'] == 4){
if(!empty($item['alias'])){
if($item['alias'] == '/'){
$item['fullUrl'] = '//' . $domain;
}else{
$item['fullUrl'] = '//' . $domain . $item['alias'].'.'.config('route.url_html_suffix');
}
}else{
$item['fullUrl'] = '//' . $domain . '/list/index/'.$item['id'].'.'.config('route.url_html_suffix');
}
}else if($item['type'] == 3){
$item['fullUrl'] = $item['alias'];
}else{
$item['fullUrl'] = 'javascript:;';
}
$item['is_design'] = 0;
$item['design_path'] = '';
if (isset($newDesignFileList[$item['list_tpl']])) {
$item['is_design'] = 1;
$item['design_path'] = $newDesignFileList[$item['list_tpl']]['design_path'];
}
}
$categoryList = makeTree($categoryList);
return jsonReturn(0,'success',$categoryList);
}
/**
* 保存新建的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \app\exception\ModelEmptyException
*/
public function save(CategoryService $categoryService,Category $Category): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
$param['alias'] = $param['alias'] ?? '';
$param['module_id'] = $param['module_id'] ?? 1;
// 数据验证
try{
if(!empty($param['parent_map'])){
$param = $this->getMapCateData($param);
}
validate(CategoryValidate::class)->scene('save')->check($param);
}catch(ValidateException $e){
return jsonReturn(-1, $e->getError());
}
if (in_array($param['alias'], StaticFileService::$forbidNames)) {
return jsonReturn(-2,lang('别名已被系统占用,请修改别名'));
}
$param['seller_id'] = $this->admin['seller_id'];
$category = $Category->existCategory(['title'=>$param['title'],'seller_id'=>$param['seller_id'],'website_id'=>$param['website_id'],'lang'=>$param['lang'],'parent_id'=>$param['parent_id']])['data'];
if(!empty($category)){
return jsonReturn(-2,'栏目已经存在');
}
if(in_array($param['type'],[1,4]) && empty($param['alias'])){
if($param['list_tpl'] == 'index'){
$param['alias'] = '/';
}else{
$param['alias'] = $this->getAlias($param['title'],$param['website_id'],$param['lang'],$param['seller_id']);
}
}
if(!empty($param['alias']) && $param['alias'] != 'javascript:;'){
$category = $Category->existCategory(['title'=>$param['alias'],'seller_id'=>$param['seller_id'],'website_id'=>$param['website_id'],'lang'=>$param['lang']])['data'];
}
if(!empty($category)){
return jsonReturn(-3,lang('栏目别名已经存在'));
}
if ($param['need_check'] == 1 && (empty($param['check_list']) || !is_array($param['check_list']))) {
return jsonReturn(-3,'请设置审核人员!');
}
if (!empty($param['field_list'])) {
$keyArr = [];
foreach ($param['field_list'] as $value) {
if (isset($keyArr[$value['key']])) {
return jsonReturn(-5,lang('自定义字段名称重复:') . $value['key']);
}
$keyArr[$value['key']] = $value;
}
$param['field_list'] = json_encode($param['field_list'], JSON_UNESCAPED_UNICODE);
}
return $categoryService -> createCategory($param);
}
return jsonReturn(-3,Lang::get(lang('请求方法错误')));
}
/**
* 栏目详情
*
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \app\exception\ModelEmptyException
*/
public function read(Category $category,Attachment $attachment): \think\response\Json
{
$param = input('param.');
try {
validate(CategoryValidate::class)->scene('read')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
$param['seller_id']= $this->admin['seller_id'];
$with = [
'thumbnail' => function($query){
$query->field('id,name,url');
},'banner' => function($query){
$query->field('id,name,url');
}
];
$res = $category->getCategory($param,$with);
if (!empty($res['data']['content'])) {
$res['data']['content'] = htmlspecialchars_decode($res['data']['content']);
}
$res['data']['check_list'] = [];
if ($res['data']['need_check'] == 1) {
$checkModel = new CategoryCheck();
$checkList = $checkModel->where([
['category_id', '=', $res['data']['id']],
['status', '=', 1]
])->select()->toArray();
$res['data']['check_list'] = $checkList;
}
if (!empty($res['data']['field_list'])) {
$res['data']['field_list'] = json_decode($res['data']['field_list'], true);
}
return json($res);
}
/**
* 保存更新的资源
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \app\exception\ModelEmptyException
*/
public function update(CategoryService $categoryService,Category $Category): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
try {
if(!empty($param['parent_map'])){
$param = $this->getMapCateData($param);
}
validate(CategoryValidate::class)->scene('update')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
if (in_array($param['alias'], StaticFileService::$forbidNames)) {
return jsonReturn(-2,lang('别名已被系统占用,请修改别名'));
}
$param['seller_id'] = $this->admin['seller_id'];
if(in_array($param['type'],[1,4]) && empty($param['alias'])){
if($param['list_tpl'] == 'index'){
$param['alias'] = '/';
}else{
$param['alias'] = $this->getAlias($param['title'],$param['website_id'],$param['lang'],$param['seller_id']);
}
}
$category = $Category->existCategory(['title'=>$param['title'],'seller_id'=>$param['seller_id'],'website_id'=>$param['website_id'],'lang'=>$param['lang'],'parent_id'=>$param['parent_id']])['data'];
if(!empty($category) && $category['id'] != $param['id']){
return jsonReturn(-2,lang('栏目已经存在'));
}
$category = $Category->existCategory(['title'=>$param['alias'],'seller_id'=>$param['seller_id'],'website_id'=>$param['website_id']])['data'];
if(!empty($category) && $category['id'] != $param['id']){
return jsonReturn(-3,lang('栏目别名已经存在'));
}
if (isset($param['need_check']) && $param['need_check'] == 1 && (empty($param['check_list']) || !is_array($param['check_list']))) {
return jsonReturn(-3,lang('请设置审核人员'));
}
if (!empty($param['field_list'])) {
$keyArr = [];
foreach ($param['field_list'] as $value) {
if (isset($keyArr[$value['key']])) {
return jsonReturn(-5,lang('自定义字段名称重复:') . $value['key']);
}
$keyArr[$value['key']] = $value;
}
$param['field_list'] = json_encode($param['field_list'], JSON_UNESCAPED_UNICODE);
}
$this->deleteCateCacheKey($param['website_id'],$param['lang']);
return $categoryService -> updateCategory($param);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 删除栏目
* @param Category $category
* @param Route $route
* @return \think\response\Json
*/
public function delete(Category $category,Route $route): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
try {
validate(CategoryValidate::class)->scene('delete')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
$where = [
'id' => $param['id'],
'seller_id' => $this->admin['seller_id'],
'website_id' => $param['website_id'],
'lang' => $param['lang']
];
Db::startTrans();
try {
$subCate = $category -> getAllCustomArrayData(['seller_id'=>$this->admin['seller_id'],'parent_id'=>$param['id']])['data'];
$this->deleteCateCacheKey($param['website_id'],$param['lang']);
if(!empty($subCate)){
return jsonReturn(-1,lang('栏目下面有子栏目,不能直接删除'));
}
$res = $category->delCategory($where);
$route->delRoute(['seller_id' => $this->admin['seller_id'],'category_id'=>$param['id']]);
$route->getRoutes($param['website_id'], $this->admin['seller_id'], $param['lang']);
CacheService::deleteRelationCacheByObject($route);
CacheService::deleteRelationCacheByObject($category);
$optAdmin = request()->admin;
event("AdminOptLog",[
'admin_id' => $optAdmin['uid'],
'admin_name' => $optAdmin['name'],
'title'=>lang("栏目管理"),
"content"=>lang("删除栏目,栏目ID为").$param['id'],
]);
Db::commit();
Cache::clear();
}catch (ModelException $me){
Db::rollback();
return jsonReturn(50023,$me->getMessage());
}catch (\Exception $e){
Db::rollback();
return jsonReturn(50024,$e->getMessage());
}
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 分组获取栏目列表
* @param Category $Category
* @return \think\response\Json
*/
public function getModuleCate(Category $Category): \think\response\Json
{
$param = input('get.');
try {
validate(CategoryValidate::class)->scene('getModuleCate')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
$Website = new Website();
$website = $Website->getAllCustomArrayData([['id','in',$param['site_id']],['seller_id','=',$this->admin['seller_id']]]
,'id asc','id,seller_id,title,domain')['data'];
$website = getColumnForKeyArray($website,'id');
$category = $Category -> getAllCustomArrayData([['website_id','in',$param['site_id']],['seller_id','=',$this->admin['seller_id']],['lang','=',$param['lang']]],'id asc')['data'];
$category = getArrayGroupBy($category,'website_id');
$response = [];
foreach($category as $key => $cate){
$tmpCategory = $this->makeTree($cate);
$tmpCategory = $this->imp($tmpCategory);
$tmpCate = $website[$key];
$tmpCate['category'] = $tmpCategory;
$response[] = $tmpCate;
}
return jsonReturn(0,lang('成功'),$response);
}
public function makeTree($data, int $pid=0, int $level=0): array
{
$tree = array();
$tmpTree = [];
foreach ($data as $key => &$value) {
$str = str_repeat('--',$level);
$value['title'] = $str . $value['title'];
if ($value['parent_id'] == $pid) {
$value['level'] = $level;
unset($data[$key]);
$tmpTree[] = $value;
$value['children'] = $this->makeTree($data, $value['id'],$level+1);
$tree[] = $value;
}else{
$tmpTree[] = $value;
}
}
return $tree;
}
public function imp($tree, $children='children'): array
{
$impArr = array();
foreach($tree as $w) {
if(isset($w[$children])) {
$t = $w[$children];
unset($w[$children]);
$impArr[] = $w;
if(is_array($t)) $impArr = array_merge($impArr, $this->imp($t, $children));
} else {
$impArr[] = $w;
}
}
return $impArr;
}
/**
* 栏目复制
* @throws ModelException
* @throws \Exception
*/
public function copy(CategoryService $categoryService): \think\response\Json
{
set_time_limit(300);
$param = input('post.');
try {
validate(CategoryValidate::class)->scene('copy')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
foreach ($param['target_site'] as $val){
if(empty($val) || !is_numeric($val)){
return jsonReturn(-1,lang('目标站点数据异常,请检查'));
}
}
$res = $categoryService -> copyCate($param['original_site'],$param['target_site'],$param['lang'],$this->admin['seller_id']);
return json($res);
}
/**
* @throws \app\exception\ModelEmptyException
* @throws ModelException
*/
public function getMapCateData(&$param)
{
$Category = new Category();
$param['parent_path'] = json_encode($param['parent_map']);
$param['parent_map'] = array_pop($param['parent_map']);
$mapCate = $Category->getCategory(['id'=>$param['parent_map'],'seller_id'=>$this->admin['seller_id']])['data']->toArray();
foreach($mapCate as $k => $v){
if($k == 'parent_id'){
continue;
}
if(empty($param[$k]) && !in_array($k,['id','create_time','update_time'])){
$param[$k] = $v;
}
}
return $param;
}
public function deleteCateCacheKey($siteId,$lang)
{
$cateCacheKey = 'hc_cate_' . $this->admin['seller_id'] . '_' . $siteId .'_' . $lang;
Cache::delete($cateCacheKey);
$moduleCateArr = Cache::get($this->cacheKeyArr);
if(!empty($moduleCateArr)){
foreach ($moduleCateArr as $cate){
$tmpCate = explode('_',str_replace('hc_module_cate_' . $this->admin['seller_id'] . '_' . $lang .'_','',$cate));
if(in_array($siteId,$tmpCate)){
$moduleCateCacheKey = 'hc_module_cate_' . $this->admin['seller_id'] . '_' . $lang .'_' . $siteId;
Cache::delete($moduleCateCacheKey);
unset($moduleCateArr[$cate]);
}
}
Cache::set($this->cacheKeyArr,$moduleCateArr);
}
}
/**
* @throws ModelException
* @throws \app\exception\ModelEmptyException
*/
public function getAlias($title, $siteId, $lang, $sellerId): string
{
$pinyin = new Pinyin();
$cacheKey = $sellerId .'_'.$siteId .'_'. $lang . '_website_cache_key';
$settingData = Cache::get($cacheKey);
if(empty($settingData)){
$settingData = ApiService::setWebsiteSetting($sellerId,$siteId,$lang);
}
if(!empty($settingData['alias_rule']) && $settingData['alias_rule'] == 1){
$alias = strtolower('/' . $pinyin -> abbr($title));
}else{
$alias = strtolower('/' . $pinyin -> permalink($title,''));
}
return $alias;
}
}

View File

@ -0,0 +1,685 @@
<?php
declare (strict_types = 1);
namespace app\controller\backend;
use app\exception\ModelEmptyException;
use app\exception\ModelException;
use app\model\InnerChart;
use app\model\RecycleBin;
use app\model\SubContent;
use app\model\SubContentCheck;
use app\model\SubContentCheckStep;
use app\model\SysSetting;
use app\service\CacheService;
use app\service\CiZhuaService;
use app\service\ContentService;
use app\service\ModuleFieldService;
use app\service\TranslateService;
use app\validate\ContentValidate;
use think\db\exception\DbException;
use think\Exception;
use think\exception\ValidateException;
use think\facade\Cache;
use think\facade\Db;
use think\facade\Lang;
use Vtiful\Kernel\Excel;
use function Complex\csc;
class ContentController extends BaseController
{
// 文章批量上传
public function articleUpload(ContentService $contentService): \think\response\Json
{
if($this->request->isPost()){
$param = $this->request->param();
$file = $this->request->file('file');
$param['file'] = $file;
if(is_string($param['category_id'])){
$param['category_id'] = explode(',', $param['category_id']);
}
try{
validate(ContentValidate::class)->scene('articleUpload')->check($param);
}catch (ValidateException $e){
return jsonReturn(-1,$e->getError());
}
$param['admin'] = $this->admin;
$res = $contentService->articleUpload($param);
CacheService::clear();
return json($res);
}
return jsonReturn(-1,Lang::get('请求方式错误'));
}
// 提取文章简介
public function descExtraction(ContentService $contentService): \think\response\Json
{
$content = input('content');
$res = $contentService -> descExtraction($content);
return jsonReturn(0,lang('成功'),$res);
}
// 图片本地化
public function imgLocal(ContentService $contentService): \think\response\Json
{
$content = input('content');
$website = input('website');
$res = $contentService -> imgExtraction($content,$this->admin['seller_id'],$website);
return jsonReturn(0,lang('成功'),$res);
}
public function wordSearch(CiZhuaService $ciZhuaService)
{
$content = input('content');
$sysSetting = new SysSetting();
$key = $sysSetting->getSysSettingValue([['title','=','cizhua_secret']])['data'];
if (empty($key)) {
return jsonReturn(-1,lang('没有配置密钥,请去系统设置-第三方设置中填写词爪密钥'));
}
$res = $ciZhuaService->wordSearch($key, $content);
$res = json_decode($res, true);
if ($res['code'] != 0) {
return json($res);
}
return jsonReturn(0,lang('成功'), $res['data']);
}
/**
* 内容添加内链
* @throws DbException
*/
public function addInnerChat(ContentService $contentService): \think\response\Json
{
$param = input('post.');
try{
validate(ContentValidate::class)->scene('addInnerChat')->check($param);
}catch (ValidateException $e){
return jsonReturn(422, $e->getError());
}
$sellerId= $this->admin['seller_id'];
$InnerChart = new InnerChart();
$innerChart = $InnerChart->getAllInnerChart(['seller_id'=>$sellerId,'website_id'=>$param['website_id'],'status'=>1],'weight desc','id,weight,keyword,url,new_page_open')['data'];
if(empty($innerChart)){
return jsonReturn(0,lang('成功'),$param['content']);
}
$subId = (int)input('sub_id');
if($subId){
$SubContent = new SubContent();
$subContent = $SubContent -> getSubContent(['id'=>$subId,'seller_id'=>$this->admin['seller_id'],'is_del'=>1])['data'];
}else{
$subContent = null;
}
$content = $contentService->autoLinks($param['content'],$innerChart,$subContent);
CacheService::clear();
return jsonReturn(0,lang('成功'),$content);
}
/**
* 文章列表
*
* @return \think\Response
*/
public function index(ContentService $contentService): \think\Response
{
$status = input('param.status');
$categoryIds = input('param.category_id');
$title = input('param.title');
$limit = $this->setLimit();
$param = [
'seller_id' => $this->admin['seller_id'],
'category_ids' => $categoryIds,
'limit' => $limit,
'lang' => input('param.lang'),
'website_id' => (int)input('param.website_id'),
'title' => $title,
'is_del' => 1,
];
$where = [];
switch ($status) {
case 1://已发布
$where[] = ['status', '=', 1];
$where[] = ['publish_time', '<=', date('Y-m-d H:i:s')];
$where[] = ['check_status', '=', 3];
break;
case 2://待发布
$where[] = ['status', '=', 1];
$where[] = ['publish_time', '>', date('Y-m-d H:i:s')];
$where[] = ['check_status', '=', 3];
break;
case 3://待审核
$where[] = ['status', '=', 1];
$where[] = ['check_status', 'in', [1,2]];
break;
case 4://被驳回
$where[] = ['status', '=', 1];
$where[] = ['check_status', '=', 4];
break;
case 5://已下线
$where[] = ['status', '=', 2];
break;
case 6://草稿
$where[] = ['status', '=', 3];
break;
}
$param['where'] = $where;
try {
$res = $contentService->indexContent($param);
} catch (ModelEmptyException | ModelException | DbException $e) {
return jsonReturn(50013,$e->getMessage());
}
return $res;
}
/**
* 待审核列表
* @return \think\response\Json
*/
public function checkList(ContentService $contentService)
{
$categoryIds = input('param.category_id');
$title = input('param.title');
$type = $this->request->param('type', 1);
$contentCheckModel = new SubContentCheck();
if ($type == 1) {
// 获取需要自己审核的待审核内容id
$ids = $contentCheckModel->where([
'check_user_id' => $this->admin['uid'],
'status' => 2,
])->column('real_id');
$contentWhere[] = ['id', 'in', $ids];
$contentWhere[] = ['check_status', 'in', [1,2]];
} else {
// 获取自己提交的需要审核的内容id
$contentWhere[] = ['admin_id', '=', $this->admin['uid']];
}
$contentWhere[] = ['status', 'in', [1,2]];
$limit = $this->setLimit();
$param = [
'seller_id' => $this->admin['seller_id'],
'category_ids' => $categoryIds,
'limit' => $limit,
'lang' => input('param.lang'),
'website_id' => (int)input('param.website_id'),
'title' => $title,
'is_del' => 1,
'where' => $contentWhere
];
try {
$res = $contentService->indexContent($param);
} catch (ModelEmptyException | ModelException | DbException $e) {
return jsonReturn(50013,$e->getMessage());
}
return $res;
}
/**
* 回收站列表
* @return \think\response\Json
*/
public function recycleBinList(ContentService $contentService)
{
$categoryIds = input('param.category_id');
$title = input('param.title');
$limit = $this->setLimit();
$param = [
'seller_id' => $this->admin['seller_id'],
'category_ids' => $categoryIds,
'limit' => $limit,
'lang' => input('param.lang'),
'website_id' => (int)input('param.website_id'),
'title' => $title,
'is_del' => 2,
];
try {
$res = $contentService->indexContent($param);
} catch (ModelEmptyException | ModelException | DbException $e) {
return jsonReturn(50013,$e->getMessage());
}
return $res;
}
/**
* 批量审核
* @return \think\response\Json
*/
public function checkAll()
{
$param = $this->request->only([
'check_data' => [],
'status' => 3,
'reason' => '',
'files' => '',
]);
// 内容多版本审核
if (empty($param['check_data']) || !in_array($param['status'], [3,4])) {
return jsonReturn(-1, lang('参数错误'));
}
if (!is_array($param['check_data'])) {
return jsonReturn(-1, lang('参数错误'));
}
if (is_array($param['files'])) {
$param['files'] = implode(',', $param['files']);
}
Db::startTrans();
try {
$contentService = new ContentService();
foreach ($param['check_data'] as $check) {
$newParam = $param;
unset($newParam['check_data']);
$newParam['real_id'] = $check['id'];
$newParam['version'] = $check['version'];
$newParam['admin'] = $this->admin;
$res = $contentService->check($newParam);
if ($res['code'] != 0) {
throw new Exception($check['id'] . lang('审核失败!版本:')."{$check['version']}" . $res['code'] . $res['msg']);
}
}
} catch (\Exception $e) {
Db::rollback();
return jsonReturn(-3, $e->getMessage(), $e->getTrace());
}
Db::commit();
return jsonReturn(0, lang('操作成功'));
}
public function editStatus(ContentService $contentService)
{
$param = $this->request->only([
'id' => [],
'status' => 1,
]);
$param['seller_id'] = $this->admin['seller_id'];
return json($contentService->editStatus($param));
}
/*
* 批量删除
*/
public function delAll(ContentService $contentService)
{
$param = $this->request->only(['ids']);
if (!is_array($param['ids'])) {
$param['ids'] = explode(',', $param['ids']);
}
$subContentModel = new SubContent();
$list = $subContentModel->where('id', 'in', $param['ids'])->column('id, module_id');
foreach ($list as $v) {
$v['seller_id'] = $this->admin['seller_id'];
$v['admin_id'] = $this->admin['uid'];
$v['name'] = $this->admin['name'];
$res = $contentService->deleteContent($v);
CacheService::clear();
}
return $res;
}
// 批量恢复
public function recoverAll(ModuleFieldService $fieldService)
{
$param = $this->request->only(['ids']);
if (!is_array($param['ids'])) {
$param['ids'] = explode(',', $param['ids']);
}
$subContentModel = new SubContent();
$list = $subContentModel->where('id', 'in', $param['ids'])->column('id, module_id');
$ids = array_column($list, 'id');
$module_ids = array_column($list, 'module_id');
$recycleBin = new RecycleBin();
$idArr = $recycleBin->where([
['sub_id', 'in', $ids],
['module_id', 'in', $module_ids]
])->column('id');
foreach ($idArr as $id) {
$res = $fieldService->resotreData($id,$this->admin['seller_id']);
}
CacheService::clear();
return $res;
}
/**
* 获取栏目字段
*
*/
public function create(ContentService $contentService): \think\response\Json
{
$categoryId = (int)input('category_id');
if(!$categoryId){
return jsonReturn(-1,lang('栏目ID不能为空'));
}
$param = [
'seller_id' => $this->admin['seller_id'],
'category_id' => $categoryId
];
try {
$res = $contentService->createContent($param);
} catch (ModelEmptyException | ModelException $e) {
return jsonReturn(-3,$e->getMessage());
}
return $res;
}
/**
* 栏目内容新增
*
* @return \think\Response
*/
public function save(ContentService $contentService): \think\Response
{
if(!request()->isPost()){
return jsonReturn(-2,lang('请求方法错误'));
}
$param = input('post.');
try{
validate(ContentValidate::class)->scene('save')->check($param);
}catch (ValidateException $e){
return jsonReturn(422, $e->getError());
}
$param['admin'] = $this->admin;
unset($param['main_content']['id']);//添加接口以防冲突不需要id
unset($param['sub_content']['id']);//添加接口以防冲突不需要id
if(empty($param['sub_content']['publish_time'])){
$param['sub_content']['publish_time'] = date('Y-m-d H:i:s',time());
}
if(!isset($param['main_content'])){
$param['main_content'] = [];
}
try {
$res = $contentService->saveContent($param);
} catch (ModelEmptyException | ModelException $e) {
return jsonReturn(-3,$e->getMessage());
}
CacheService::clear();
return json($res);
}
/**
* 显示指定的资源
*
*/
public function read(ContentService $contentService): \think\response\Json
{
$param = request()->only(['id','module_id','website_id','lang']);
try {
validate(ContentValidate::class)->scene('read')->check($param);
}catch(ValidateException $e){
return jsonReturn(-422,$e->getError());
}
$param['seller_id'] = $this->admin['seller_id'];
try {
$res = $contentService->readContent($param);
} catch (ModelEmptyException | ModelException $e) {
return jsonReturn(50025,$e->getMessage());
}
return $res;
}
/**
* 保存更新的资源
*
* @return \think\Response
*/
public function update(ContentService $contentService): \think\Response
{
if(!request()->isPost()){
return jsonReturn(-2,lang('请求方法错误'));
}
$param = input('post.');
try{
validate(ContentValidate::class)->scene('update')->check($param);
}catch (ValidateException $e){
return jsonReturn(422, $e->getError());
}
$param['admin'] = $this->admin;
if(empty($param['sub_content']['publish_time'])){
$param['sub_content']['publish_time'] = date('Y-m-d H:i:s',time());
}
try {
$res = $contentService->updateContent($param);
} catch (ModelEmptyException | ModelException $e) {
return jsonReturn(-3,$e->getMessage());
}
CacheService::clear();
return $res;
}
/**
* 删除指定资源
*
*/
public function delete(ContentService $contentService): \think\response\Json
{
$param = request()->only(['id','module_id']);
try {
validate(ContentValidate::class)->scene('delete')->check($param);
}catch(ValidateException $e){
return jsonReturn(-422,$e->getMessage());
}
$param['seller_id'] = $this->admin['seller_id'];
$param['admin_id'] = $this->admin['uid'];
$param['name'] = $this->admin['name'];
CacheService::clear();
return $contentService->deleteContent($param);
}
public function mainContent(ContentService $contentService): \think\response\Json
{
if(!request()->isGet()){
return jsonReturn(-1,lang('请求方法错误'));
}
$param = request()->param(['module_id','website_id','page','limit']);
try {
validate(ContentValidate::class)->scene('mainContent')->check($param);
}catch(ValidateException $e){
return jsonReturn(-422,$e->getMessage());
}
$limit = (int)input('param.limit') ?: 10;
$param = [
'seller_id' => $this->admin['seller_id'],
'website_id' => $param['website_id'],
'module_id' => $param['module_id'],
'limit' => $limit,
];
try {
$res = $contentService->mainContent($param);
} catch (ModelEmptyException | ModelException | DbException $e) {
return jsonReturn(50013,$e->getMessage());
}
return $res;
}
/**
* 修改置顶
* @throws ModelException
*/
public function topOpt(ContentService $contentService): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
try {
validate(ContentValidate::class)->scene('topOpt')->check($param);
}catch(ValidateException $e){
return jsonReturn(-422,$e->getMessage());
}
$param['seller_id'] = $this->admin['seller_id'];
return $contentService->topOpt($param);
}
return jsonReturn(-1,lang('请求方法错误'));
}
/**
* 推荐和修改状态
* @throws ModelException
*/
public function recommendAndStatus(ContentService $contentService): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
try {
validate(ContentValidate::class)->scene('recommendAndStatus')->check($param);
}catch(ValidateException $e){
return jsonReturn(-422,$e->getMessage());
}
$param['seller_id'] = $this->admin['seller_id'];
CacheService::clear();
return $contentService->recommendAndStatus($param);
}
return jsonReturn(-1,lang('请求方法错误'));
}
public function moduleContent(ContentService $contentService): \think\response\Json
{
// 关联内容
$table = input('param.table');
// $field = input('param.field');
if(empty($table)){
return jsonReturn(-1,lang('表名不能为空'));
}
// if(empty($field)){
// return jsonReturn(-2,'关联字段不能为空');
// }
$res = $contentService -> getModuleContent($table,$this->admin['seller_id']);
return jsonReturn($res);
}
/**
* excel导入内容
*/
public function importExcel()
{
$lang = $this->request->param('lang/s',"zh");
if(empty($_FILES['file'])){
return jsonReturn(-1,lang("请传入文件"));
}
$fileName = $_FILES['file'];
$temp_file = sys_get_temp_dir();
$config = ['path' => $temp_file];
$excel = new Excel($config);
$tmpName = strrchr($fileName['tmp_name'], '/');
//读取文件
$sheetList = $excel->openFile($tmpName)->sheetList();
if (empty($sheetList)) {
return jsonReturn(-1, lang('上传文件为空'));
}
$sheetData = $excel
->openSheet($sheetList[0], Excel::SKIP_EMPTY_ROW)->getSheetData();
if (!isset($sheetData[1]) || !isset($sheetData[2])) {
return jsonReturn(-1, lang('上传文件为空'));
}
$fieldLocation = array();
foreach ($sheetData[0] as $key => $value) {
$fieldLocation[$value] = $key;
}
unset($sheetData[0]);
$needField = ['标题', '链接', '网站', '类别ID','类别','图片'];
foreach ($needField as $key => $value) {
if (!isset($fieldLocation[$value])) {
return jsonReturn(-1, lang('缺少必要列:') . $value);
}
}
//查询栏目id和内容
$category = Db::name("category")->where(['seller_id'=>$this->admin['seller_id'],'lang'=>$lang])
->column("title","id");
if(empty($category)){
return jsonReturn(-5, lang("请先添加栏目"));
}
try{
$insertData= $files = $filesUrl = $uploadFiles = [];
foreach ($sheetData as $k=>$v){
if(empty($v[0])){
return jsonReturn(-4,lang("标题不能为空"));
}
if(!empty($v[5]) && !preg_match('/(http:\/\/)|(https:\/\/)/i', $v[5])){
return jsonReturn(-4,lang("图片格式错误"));
}
if(empty($category[$v[3]])){
return jsonReturn(-4,$v[3].lang('对应的分类不存在')."[{$v[4]}]");
}
if (!empty($v[5]) && !in_array(trim($v[5]),$files)){
$files[]=trim($v[5]);
$uploadFiles[$k]['url'] = trim($v[5]);
$uploadFiles[$k]['seller_id'] = $this->admin['seller_id'];
$uploadFiles[$k]['name'] = $v[4];
$uploadFiles[$k]['type'] = 2;
$uploadFiles[$k]['storage'] = 1;
}
$insertData[$k]['seller_id'] = $this->admin['seller_id'];
$insertData[$k]['category_id'] = $v[3];
$insertData[$k]['module_id'] = 1;
$insertData[$k]['lang'] = $lang;
$insertData[$k]['title'] = $v[0];
$insertData[$k]['thumbnail'] = $v[5];
$insertData[$k]['admin_id'] = $this->admin['uid'];
$insertData[$k]['author'] = $v[2];
$insertData[$k]['check_status'] = 3;
$insertData[$k]['url'] = $v[1];
$insertData[$k]['publish_time'] = date("Y-m-d H:i:s");
$insertData[$k]['create_time'] =time();
}
}catch (\Exception $e){
throw new ModelException($e->getMessage(),-1);
}
Db::startTrans();
try{
if(!empty($uploadFiles)){
Db::name("attachment")->insertAll($uploadFiles);
$filesUrl = Db::name("attachment")->where([['url',"in",$files]])->column("id","url");
}
foreach ($insertData as $ki=>$data){
$data['thumbnail'] = isset($filesUrl[$data['thumbnail']])?$filesUrl[$data['thumbnail']]:0;
$sub_id = Db::name("sub_content")->insertGetId($data); //文章的id
Db::name("category_sub_content")->insert([
"seller_id"=>$data['seller_id'],
"category_id"=>$data['category_id'],
"sub_content_id"=>$sub_id,
]);
$mainId = Db::name("article")->insertGetId([
"seller_id"=>$data['seller_id'],
"lang"=>$lang,
"sub_id"=>$sub_id,
"create_time"=>time()
]);
Db::name("sub_content")->where(['id'=>$sub_id])->update(['main_id'=>$mainId]);
}
Db::commit();
}catch (\Exception $e){
Db::rollback();
throw new ModelException($e->getMessage(),-2);
}
return jsonReturn(0,lang('导入成功'));
}
}

View File

@ -0,0 +1,146 @@
<?php
declare (strict_types = 1);
namespace app\controller\backend;
use app\model\ContentTag;
use app\taglib\HcTaglib;
use app\validate\ContentTagValidate;
use think\exception\ValidateException;
use think\facade\Lang;
class ContentTagController extends BaseController
{
public function initialize()
{
define('ADMIN_SITE_ID',(int)input('site_id'));
define('ADMIN_SELLER_ID',$this->admin['seller_id']);
parent::initialize();
}
/**
* 显示资源列表
*
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function index(ContentTag $contentTag): \think\response\Json
{
$where = [
'seller_id' => $this->admin['seller_id'],
'is_del' => 1,
];
$limit = 10;
$limitParam = (int)input('limit');
if($limitParam){
$limit = $limitParam;
}
// TODO
// 添加其他逻辑
$contentTagList = $contentTag->getContentTagList($where,$limit);
return json(pageReturn($contentTagList));
}
/**
* 保存新建的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function save(ContentTag $contentTag): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
// 数据验证
try{
validate(ContentTagValidate::class)->scene('save')->check($param);
}catch(ValidateException $e){
return jsonReturn(-1, $e->getError());
}
// TODO
// 其他逻辑
$res = $contentTag -> addContentTag($param);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 显示指定的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \app\exception\ModelEmptyException
*/
public function read(HcTaglib $hcTaglib): \think\response\Json
{
$param = input('post.');
try {
validate(ContentTagValidate::class)->scene('read')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
$param['seller_id'] = $this->admin['seller_id'];
$func = 'tag' . ucfirst($param['title']);
// $hcTaglib = new HcTaglib(new Template());
$hcTaglib->$func();
dd($param);
}
/**
* 保存更新的资源
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function update(ContentTag $contentTag): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
try {
validate(ContentTagValidate::class)->scene('update')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
$where = [
'id' => $param['id'],
'seller_id' => $this->admin['seller_id'],
'is_del' => 1,
];
$res = $contentTag -> updateContentTag($where,$param);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 删除指定资源
*
* @throws \app\exception\ModelException
*/
public function delete(ContentTag $contentTag): \think\response\Json
{
if(request()->isPost()){
$id = (int)input('id');
if(!$id){
// TO DO
// 替换错误提示
return jsonReturn(-1,'ErrorMsg');
}
$where = [
'id' => $id,
'seller_id' => $this->admin['seller_id']
];
$res = $contentTag->delContentTag($where);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
}

View File

@ -0,0 +1,68 @@
<?php
declare (strict_types = 1);
namespace app\controller\backend;
use think\facade\Db;
class DashboardController extends BaseController
{
/**
* 后台首页看板
*
*/
public function index(): \think\Response
{
if(!hcInstalled()){
createInstallFile();
}
$install_date = filectime(app()->getRootPath().'public/system_file/install.lock');
// 运行天数
$total_day = (int)ceil((time() - $install_date) / (24*3600)) ?: 1;
// 总站点数
$site_count = Db::name('website')->where('seller_id',$this->admin['seller_id'])->count('id');
// 总访客数
$visit_count = count(Db::name('visit_log')->distinct(true)->field('ip,visited_time')->select()->toArray());
// 总文章数 (模型为系统文章模型)
$article_count = Db::name('sub_content')->where('is_del',1)->count('id');
// 总询盘数
$inquiry_count = Db::name('inquiry')->where('is_del',1)->count('id');
// 总关键词数
$keyword_count = Db::name('keyword')->where('is_del',1)->count('id');
// 总导入链接数
$in_link_count = Db::name('link')->where(['type'=>1,'is_del'=>1])->count('id');
// 总导出链接数
$out_link_count = Db::name('link')->where(['type'=>2,'is_del'=>1])->count('id');
// 授权信息 // https://www.huocms.com/auth
$domain = request()->param('domain');
if(empty($domain)){
$auth_info = null;
}else{
$authRes = curlPost(config('system.auth_query_url'),['domain'=> request()->param('domain')])['data'];
$auth_info = json_decode($authRes,true);
if(empty($auth_info['data'])){
$auth_info = null;
}else{
$auth_info = $auth_info['data'];
}
$websiteTitle = Db::name('website')->where('domain',$domain)->where('seller_id',$this->admin['seller_id'])->value('title');
}
return jsonReturn(0,'success',[
'total_day' => $total_day,
'site_count' => $site_count,
'visit_count' => $visit_count,
'article_count' => $article_count,
'inquiry_count' => $inquiry_count,
'keyword_count' => $keyword_count,
'in_link_count' => $in_link_count,
'out_link_count' => $out_link_count,
'auth_info' => $auth_info,
'version' => file_get_contents(app()->getRootPath().'version'),
'website_title' => $websiteTitle ?? '',
]);
}
}

View File

@ -0,0 +1,146 @@
<?php
namespace app\controller\backend;
use app\model\Database;
use app\service\MysqlBackupService;
use think\App;
use think\facade\Db;
use think\facade\Env;
class DatabaseController extends BaseController
{
protected $service;
/**
* @throws \Exception
*/
public function __construct(App $app)
{
parent::__construct($app);
$config = array(
'level' => 5,//数据库备份卷大小
'compress' => 0,//数据库备份文件是否启用压缩 0不压缩 1 压缩
);
$this->service = new MysqlBackupService($config);
}
/**
* 数据表列表
* @return \think\response\Json
*/
public function tableList(): \think\response\Json
{
return json($this->service->dataList());
}
/**
* 备份文件列表
* @return mixed
* @throws \app\exception\ModelException
*/
public function index()
{
$path = app()->getRootPath() .'backup/';
$glob = hcScanDir($path.'*');
if(empty($glob)){
return jsonReturn(0,'success');
}
arsort($glob);
$data = [];
foreach ($glob as $key => $val){
$tmp = [
'id' => $key+1,
'title' => $val,
'size' => sprintf("%.2f",filesize($path.$val) / 1000) .'kb' ,
];
array_push($data,$tmp);
}
return jsonReturn(0,lang('成功'),$data);
}
/**
* @param $name
* @return mixed
*/
public function detail($name)
{
return jsonReturn($this->service->detail($name));
}
/**
* 数据库备份
* @throws \app\exception\ModelException
* @throws \think\db\exception\BindParamException
*/
public function backup(): \think\response\Json
{
return $this->service->backupDatabase($this->admin['seller_id']);
}
/**
* 文件恢复
* @return \think\response\Json
*/
public function restore(): \think\response\Json
{
if(!request()->isPost()){
return jsonReturn(-1,lang('方法请求错误'));
}
$filename = input('post.filename');
$res = $this->service->import($filename,0);
return json($res);
}
/**
* 数据表优化
* @param $name
* @throws \Exception
*/
public function optimize($name): \think\response\Json
{
$this->service->optimize($name);
return jsonReturn(0,lang('优化成功'));
}
/**
* 数据表修复
* @param $name
* @throws \Exception
*/
public function repair($name): \think\response\Json
{
$this->service->repair($name);
return jsonReturn(0,lang('修复成功'));
}
/**
* @return \think\response\File
*/
public function downloadFile(): \think\response\File
{
try {
$time = intval($this->request->param('filename'));
$file =$this->service->getFile('time', $time);
$fileName = $file[0];
return download($fileName,$time);
}catch (UploadFailException $e){
return app('json')->fail(lang('下载失败'));
}
}
/**
* @throws \Exception
*/
public function delete(): \think\response\Json
{
$filename = $this->request->param('filename');
if(empty($filename)){
return jsonReturn(-2,lang('文件名不能为空'));
}
return $this->service->delFile($filename);
}
}

View File

@ -0,0 +1,667 @@
<?php
namespace app\controller\backend;
use app\model\BigField;
use app\model\Category;
use app\model\CategorySubContent;
use app\model\SubContent;
use app\model\Theme;
use app\model\ThemeFile;
use app\model\Website;
use app\model\WebsiteSetting;
use app\service\ApiService;
use app\service\ThemeFileService;
use app\service\ThemeService;
use app\validate\WebsiteSettingValidate;
use think\exception\ValidateException;
use think\response\Json;
class DesignController extends BaseController
{
protected $designBase = 'public/design/designPage';
protected function sanitizeFileName($file)
{
//sanitize, remove double dot .. and remove get parameters if any
$file = CMS_ROOT . 'public/design/designPage/' . preg_replace('@\?.*$@', '', preg_replace('@\.{2,}@', '', preg_replace('@[^\/\\a-zA-Z0-9\-\._]@', '', $file)));
return $file;
}
public function save()
{
return $this->publish(1);
// $param = $this->request->param();
// // 数据验证
// try {
// validate(WebsiteSettingValidate::class)->scene('read')->check($param);
// } catch (ValidateException $e) {
// return jsonReturn(-1, $e->getError());
// }
//
// define('MAX_FILE_LIMIT', 1024 * 1024 * 2);//2 Megabytes max html file size
//
// $html = "";
// if (isset($_POST['startTemplateUrl']) && !empty($_POST['startTemplateUrl'])) {
// $startTemplateUrl = $this->sanitizeFileName($_POST['startTemplateUrl']);
// $html = file_get_contents($startTemplateUrl);
// } else if (isset($_POST['html'])) {
// $html = substr($_POST['html'], 0, MAX_FILE_LIMIT);
// }
//
// $where = [
// 'seller_id' => $this->admin['seller_id'],
// 'website_id' => $param['website_id'],
// 'lang' => $param['lang'],
// 'is_active' => 1
// ];
// $Theme = new Theme();
// $theme = $Theme->getActiveTheme($where)['data']->toArray();
// $themeName = $theme['theme'];
//
// $pathInfo = pathinfo($param['file']);
// $filename = $pathInfo['filename'];
//// $file = $this->sanitizeFileName($_POST['file']);
// $file = CMS_ROOT . "view/website/{$param['website_id']}/{$param['lang']}/{$themeName}/single_{$filename}.html";
//
// if (file_put_contents($file, $html)) {
// echo "保存成功 $file";
// } else {
// header($_SERVER['SERVER_PROTOCOL'] . ' 500 Internal Server Error', true, 500);
// echo "保存失败! $file\n可能的原因是缺少写权限或文件路径不正确";
// }
// exit();
}
/**
* @param $saveType 1保存2发布
* @return Json|void
*/
public function publish($saveType = 2)
{
$param = $this->request->param();
// 数据验证
try {
validate(WebsiteSettingValidate::class)->scene('read')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
define('MAX_FILE_LIMIT', 1024 * 1024 * 2);//2 Megabytes max html file size
$html = "";
if (isset($param['startTemplateUrl']) && !empty($param['startTemplateUrl'])) {
$startTemplateUrl = $this->sanitizeFileName($param['startTemplateUrl']);
$html = file_get_contents($startTemplateUrl);
} else if (isset($_POST['html'])) {
$html = substr($_POST['html'], 0, MAX_FILE_LIMIT);
}
preg_match_all( '~<img.*?src=["\']+(.*?)["\']+.*>~' , $html, $match );
$imgTagArr = $match[0];
$imgSrcArr = $match[1];
$maxImgSize = config('system.lazy_load_size'); // 读取文件大小,大于 配置文件 做懒加载
$oldImgTagArr = [];
$newImgTagArr = [];
foreach ($imgSrcArr as $key => $value) {
// 存在无需懒加载的标签,跳过
if (strpos($imgTagArr[$key], 'no-lazy') !== false) {
continue;
}
if (empty($value)) {
continue;
}
if (strpos($value, 'data:') === 0) {
continue;
}
if (strpos($value, 'http:') === 0) {
$fileUrl = $value;
$res = @get_headers($fileUrl,true);
if (!isset($res['Content-Length'])) {
continue;
}
$filesize = round($res['Content-Length']/1024,2);
}
else {
$publicPath = CMS_ROOT . 'public';
if (strpos($value, '/') !== 0) {
$publicPath .= '/';
}
$fileUrl = $publicPath . $value;
if (!file_exists($fileUrl)) {
continue;
}
$filesize = filesize($fileUrl);
$filesize /= pow(1024, 1);
}
if ($filesize > $maxImgSize) {
$imgTagArr[$key] = str_replace("data-src='{$value}'", '', $imgTagArr[$key]);
$imgTagReplace = str_replace($value, '/system_file/loading.png', $imgTagArr[$key]);
$replaceStr = " data-src='{$value}' ";
$imgTagReplace = substr_replace($imgTagReplace, $replaceStr, 4, 0);
$oldImgTagArr[] = $imgTagArr[$key];
$newImgTagArr[] = $imgTagReplace;
}
}
if (!empty($oldImgTagArr)) {
$html = str_replace($oldImgTagArr, $newImgTagArr, $html);
}
$jsStr = '
<script id="loadingJs" src="/system_file/js/huocms-loading.js"></script>
</head>';
if (strpos($html, '/system_file/js/huocms-loading.js') === false) {
$html = str_replace('</head>', $jsStr, $html);
}
$html = \phpQuery::newDocument($html);
// 根据section的顺序确定各个部分插入的顺序
$html['head #viewHelp']->remove();//移除可视化时用于方便界面编辑的css
$blockHtmlArr = [];
$where = [
'seller_id' => $this->admin['seller_id'],
'website_id' => $param['website_id'],
'lang' => $param['lang'],
'is_active' => 1
];
$Theme = new Theme();
$themeFileModel = new ThemeFile();
$theme = $Theme->getActiveTheme($where)['data']->toArray();
$themeName = $theme['theme'];
$BigField = new BigField();
$version = $BigField->where([
['seller_id', '=', $this->admin['seller_id']],
['theme_id', '=', $theme['id']],
])->max('version');
$version = $version ?: 0;
$version++;
$version = intval($version);
\phpQuery::each($html['body .huocms-block'], function ($key, $item) use ($html, &$blockHtmlArr, $param, $themeName, $saveType, $theme, $version) {
$domObj = $html['.huocms-block:eq('.$key.')'];
$includePath = $domObj->attr('data-inclue-path'); // 自己有包含路径或某个子元素有包含路径,需要将代码替换掉
$components = $domObj->find('.huocms-components');
foreach ($components as $obj) {
$subIncludePath = pq($obj)->attr('data-inclue-path');
if (!empty($subIncludePath)) {
if (pq($obj)->hasClass('huocms-static-edit')) {
$content = pq($obj)->htmlOuter();
$themeFileName = "{$subIncludePath}.html";
$filename = $subIncludePath;
ThemeService::saveThemeFileHistory($param['website_id'], $param['lang'], $theme, $this->admin['seller_id'], $themeFileName, $filename, $content, '', '', $version);
}
$includeHtml = '
{include file="'.$subIncludePath.'"}
';
$next = pq($obj)->next();
$parent = pq($obj)->parent();
if (empty($next->html())) {
pq($obj)->remove();
$parent->append($includeHtml);
} else {
pq($obj)->remove();
$next->before($includeHtml);
}
}
}
$blockHtml = '';
if (!empty($includePath)) {
if ($domObj->hasClass('huocms-static-edit')) {
$content = $domObj->htmlOuter();
$themeFileName = "{$includePath}.html";
$filename = $includePath;
ThemeService::saveThemeFileHistory($param['website_id'], $param['lang'], $theme, $this->admin['seller_id'], $themeFileName, $filename, $content, '', '', $version);
}
$domObj = '
{include file="'.$includePath.'"}
';
$blockHtml = $domObj;
$blockHtmlArr[] = $blockHtml;
return $item;
}
$tmpTemplate = $domObj->attr('data-huocms-block');
$blockId = $domObj->attr('data-huocms-blockid');
$blockType = $this->getBlockType($domObj);
// echo $blockType . '<br/>';
if (!empty($tmpTemplate) && !empty($blockId)) {
$blockTemplatePath = CMS_ROOT . $this->designBase . DIRECTORY_SEPARATOR . $tmpTemplate;
$blockHtml = file_get_contents($blockTemplatePath);
$blockHtml = $this->replaceHtml($blockType, $blockId, $blockHtml, $domObj);
} else {
$blockHtml = $domObj;
}
$blockHtmlArr[] = $blockHtml;
return $item;
});
$html['body .huocms-block']->remove();
$html['body #think_page_trace']->remove();
$html['body #think_page_trace_open']->remove();
$blockHtmlArr = array_reverse($blockHtmlArr);
foreach ($blockHtmlArr as $value) {
$html['body']->prepend($value);
}
// exit();
//保存生成的html
$pathInfo = pathinfo($param['file']);
$filename = $pathInfo['filename'];
$filePath = CMS_ROOT . "public/themes/website/{$param['website_id']}/{$param['lang']}/{$themeName}/{$filename}.html";
$viewFilePath = CMS_ROOT . "public/themes/hc_original/{$themeName}/{$filename}.html";
// 替换SEO
$html['head meta[name="description"]']->remove();
$html['head meta[name="keywords"]']->remove();
$html['head title']->remove();
$html['head']->prepend($this->getSeoHtml());
$themeFileName = "{$filename}.html";
// 保存至版本数据库中
$content = $html->html();
ThemeService::saveThemeFileHistory($param['website_id'], $param['lang'], $theme, $this->admin['seller_id'], $themeFileName, $filename, $content, '', '', $version);
if ($saveType == 1) {
echo "保存成功 !";
return;
}
//更新theme_file
$themeService = new ThemeService();
$suffix = config('view.view_suffix');
$res = $themeService->publishThemeFile($theme['id'],$param['website_id'],$this->admin['seller_id'],$suffix, $version);
$res = $res->getData();
if ($res['code'] != 0) {
echo $res['msg'];
return;
}
$themeFileModel->updateThemeFile([
['file', '=', $themeFileName],
['website_id', '=', $param['website_id']],
['lang', '=', $param['lang']],
], ['is_design' => 1, 'design_path' => $themeFileName]);
echo "发布成功 !";
return;
}
protected function getBlockType($node) {
$classes = explode( ' ', $node->attr('class'));
$blockType = '';
foreach ($classes as $class) {
if ($class != 'huocms-components' && $class != 'huocms-block') {
$blockType = $class;
break;
}
}
return $blockType;
}
public function replaceHtml($blockType, $id, $html, $node)
{
if (strpos($blockType, 'block') !== false) {
// 替换模板里的navID
$blockHtml = str_replace('hcTaglib:category id="1"', 'hcTaglib:category id="' . $id . '"', $html);
} elseif ($blockType == 'huocms-nav') {
// 替换模板里的navID
$blockHtml = str_replace('hcTaglib:nav cid="2"', 'hcTaglib:nav cid="' . $id . '"', $html);
} elseif ($blockType == 'huocms-banner') {
// 替换模板里的navID
$blockHtml = str_replace('hcTaglib:slide cid="1"', 'hcTaglib:slide cid="' . $id . '"', $html);
} else {
$blockHtml = $node;
}
return $blockHtml;
}
public function getSeoHtml()
{
return '
<title>{$current_cate.seo_title|default=$current_cate.title}</title>
<meta name="description" content="{$current_cate.seo_description|default=$current_cate.title}">
<meta name="keywords" content="{$current_cate.seo_keywords|default=$current_cate.title}">';
}
// setting接口
public function getSetting()
{
}
/**
* 单个链接列表接口
* 基础链接客服链接icp备案链接公安备案链接
* 栏目链接
* 内容链接
* @return Json
*/
public function getLinkList()
{
$param = $this->request->param();
// 数据验证
try {
validate(WebsiteSettingValidate::class)->scene('read')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
$where = [
'seller_id' => $this->admin['seller_id'],
'website_id' => $param['website_id'],
'lang' => $param['lang'],
];
$websiteSetting = new WebsiteSetting();
$res = $websiteSetting->getWebsiteSetting($where);
$res['data'] = $res['data']->toArray();
$res['data']['setting'] = json_decode($res['data']['setting'], true);
$link = [
'name' => '基本链接',
'value' => '',
'child' => [
[
'name' => '客服链接',
'value' => $res['data']['setting']['customer_code'] ?? '',
],
[
'name' => 'icp备案链接',
'value' => $res['data']['setting']['icp_link'] ?? '',
],
[
'name' => '公网安备链接',
'value' => $res['data']['setting']['gwa_link'] ?? '',
],
]
];
$categoryList = $this->categoryList();
$cate = [
'name' => '栏目链接',
'value' => '',
'child' => $categoryList,
];
$data = [
'link' => $link,
'cate' => $cate,
'content' => [],
];
return jsonReturn(0, '成功', $data);
}
// 导航接口
public function getNavList()
{
}
// 友情链接接口
public function getFriendLink()
{
}
public function getCategoryList()
{
return jsonReturn(0, '成功', $this->categoryList());
}
public function categoryListSetWhere(&$where, $type)
{
switch ($type) {
case 'suq_block1':
case 'suq_block2_contact':
case 'suq_block3':
case 'suq_block4':
case 'suq_block5':
case 'suq_block6':
case 'suq_footer':
$where[] = ['type', 'in', [2,3]];
$where[] = ['id', '>', 1];
break;
case 'suq_block2':
$where[] = ['id', '>', 1];
break;
default:
break;
}
}
// 栏目列表接口
protected function categoryList()
{
$param = $this->request->param();
$where = [
['seller_id', '=', $this->admin['seller_id']],
['website_id', '=', $param['website_id']],
];
$lang = $param['lang'] ?? '';
if (empty($lang)) {
$lang = 'zh';
}
$Website = new Website();
$website = $Website->getWebsite(['id' => $param['website_id'], 'seller_id' => $this->admin['seller_id']])['data'];
$domain = $website['domain'];
if ($lang != 'zh') {
$domain = $domain . '/' . $lang;
}
$where[] = ['lang', '=', $lang];
if(isset($param['type'])) {
$this->categoryListSetWhere($where, $param['type']);
}
$cateModel = new Category();
$categoryList = $cateModel->where($where)->field('id,type,title,alias')->select()->each(function (&$item) use ($domain) {
if ($item['type'] == 1 || $item['type'] == 4) {
if (!empty($item['alias'])) {
if ($item['alias'] == '/') {
$item['fullUrl'] = 'http://' . $domain;
} else {
$item['fullUrl'] = 'http://' . $domain . $item['alias'] . '.' . config('route.url_html_suffix');
}
} else {
$item['fullUrl'] = 'http://' . $domain . '/list/index/' . $item['id'] . '.' . config('route.url_html_suffix');
}
} else if ($item['type'] == 3) {
$item['fullUrl'] = $item['alias'];
} else {
$item['fullUrl'] = 'javascript:;';
}
});
return $categoryList->toArray();
}
// 栏目内容接口
public function getCategoryData()
{
$param = $this->request->param();
$categoryModel = new Category();
$contentModel = new SubContent();
$category = [];
$subCategory = [];
//获取此栏目数据和下面所有子栏目数据,并且拿到所有栏目下的内容数据
if ($param['type'] == 'suq_block1') {
//获取选择栏目的的标题和副标题,再获取子栏目的名称和缩略图
//最后获取子栏目的内容列表
$category = $categoryModel->getCategory([
['id', '=', $param['category_id']],
['type', 'in', [2,3]],
], [], 'id, type, title, sub_title, alias')['data'];
$subCategory = $categoryModel->where([
['parent_id', '=', $param['category_id']],
['type', 'in', [2,3]],
])->with(['thumbnail'])->field('id, type, title, sub_title, desc, thumbnail, alias')->select();
foreach ($subCategory as &$subCate) {
$subCate['content'] = $contentModel->alias('a')
->join('category_sub_content b', 'b.sub_content_id = a.id', 'left')
->with(['thumbnail'])
->where([
['b.category_id', '=', $subCate['id']]
])
->field('a.id, title, thumbnail')
->select();
}
} else if ($param['type'] == 'suq_block2' ||
$param['type'] == 'suq_block3' ||
$param['type'] == 'suq_block4' ||
$param['type'] == 'suq_footer') {
$category = $categoryModel->getCategory([
['id', '=', $param['category_id']],
['type', 'in', [2,3]],
], ['thumbnail'], 'id, type, title, sub_title, desc, alias, thumbnail, description')['data'];
$contentList = $contentModel->alias('a')
->join('category_sub_content b', 'b.sub_content_id = a.id', 'left')
->with(['thumbnail'])
->where([
['b.category_id', '=', $param['category_id']]
])
->field('a.id, title, sub_title, description, thumbnail')
->select()->toArray();
foreach ($contentList as &$value) {
if (isset($value['thumbnail']['url'])) {
$value['thumbnail_url'] = $value['thumbnail']['url'];
}
if (in_array($category['type'], [1,2,4])) {
$value['href'] = hcUrl('detail/index', ['cid' => $param['category_id'], 'id' => $value['id']]);
} else {
$value['href'] = $category['alias'];
}
}
$category['content'] = $contentList;
} else if ($param['type'] == 'suq_block5' ||
$param['type'] == 'suq_block6' ||
$param['type'] == 'suq_block2_contact') {
$category = $categoryModel->getCategory([
['id', '=', $param['category_id']],
['type', 'in', [2,3]],
], ['thumbnail'], 'id, type, title, sub_title, desc, alias, thumbnail, description, seo_description')['data'];
}
$returnData = [
'category' => $category,
'subCategory' => $subCategory,
];
return jsonReturn(0, '成功', $returnData);
}
// 内容列表接口
public function getContentList()
{
}
// 内容详情接口
// 幻灯片接口
// 广告接口
// 获取模板文件历史版本
public function getHistoryList()
{
$param = $this->request->param();
$initPage = $param['init_page'] ?? 'index';
$initPageName = $initPage . '.html';
$param['limit'] = $param['limit'] ?? 100;
$themeModel = new Theme();
$themeId = $themeModel->where([
['website_id', '=', $param['website_id']],
['lang', '=', $param['lang']],
['is_active', '=', 1],
])->value('id');
if (empty($themeId)) {
return jsonReturn(-1, '当前站点没有激活的模板,请先去安装模板');
}
// 获取当前模板文件id
$themeFileModel = new ThemeFile();
$themeFileId = $themeFileModel->where([
['website_id', '=', $param['website_id']],
['lang', '=', $param['lang']],
['theme_id', '=', $themeId],
['file', '=', $initPageName],
])->value('id');
if (empty($themeFileId)) {
return jsonReturn(-2, '模板文件不存在');
}
$bigFieldModel = new BigField();
$list = $bigFieldModel->alias('a')
->join('admin b', 'b.id = a.admin_id', 'left')
->join('theme_file c', 'a.theme_file_id = c.id')
->where([
['a.theme_id', '=', $themeId],
['a.theme_file_id', '=', $themeFileId],
])->field('a.*, b.name as admin_name, c.file')->order('id desc')->paginate($param['limit']);
return json(pageReturn(dataReturn(0, '', $list)));
}
public function setPreviewPage()
{
$param = $this->request->param();
$initPage = $param['init_page'] ?? 'index';
$initPageName = $initPage . '.html';
$version = $param['version'] ?? 1; // 将此版本的所有文件都塞入view下
$themeModel = new Theme();
$theme = $themeModel->where([
['website_id', '=', $param['website_id']],
['lang', '=', $param['lang']],
['is_active', '=', 1],
])->field('id, theme')->find();
if (empty($theme['id'])) {
return jsonReturn(-1, '当前站点没有激活的模板,请先去安装模板');
}
ThemeService::updatePreviewFile($param, $version, $theme);
session('history-view', time());
return jsonReturn(0, '预览初始化成功');
}
}

View File

@ -0,0 +1,506 @@
<?php
namespace app\controller\backend;
use app\model\DiyForm;
use Overtrue\Pinyin\Pinyin;
use think\facade\Db;
class FormController extends BaseController
{
public function index(DiyForm $diyFormModel)
{
$limit = input('param.limit');
$name = input('param.name');
$where[] = ['is_del', '=', 1];
$where[] = ['seller_id', '=', $this->admin['seller_id']];
if (!empty($name)) {
$where[] = ['name', 'like', '%' . $name . '%'];
}
try {
$list = $diyFormModel->where($where)->order('id desc')->paginate($limit);
} catch (\Exception $e) {
return jsonReturn(-1, $e->getMessage());
}
return json(pageReturn(dataReturn(0, lang('成功'), $list)));
}
public function info(DiyForm $diyFormModel)
{
$id = $this->request->param('id');
$info = $diyFormModel->where('id', $id)->find();
if (empty($info)) {
return jsonReturn(-3, lang('该表单不存在'));
}
return jsonReturn(0, lang('获取成功'), $info);
}
public function add(DiyForm $diyFormModel)
{
$param = input('post.');
if (empty($param['name'])) {
return jsonReturn(-1, lang('请输入表单名称'));
}
try {
$has = $diyFormModel->where('name', $param['name'])->find();
if (!empty($has)) {
return jsonReturn(-3, lang('该表单已经存在'));
}
$pinyinModel = new Pinyin();
$tableName = $pinyinModel->abbr($param['name']);
$has = $diyFormModel->where('table', $tableName)->find();
if (!empty($has)) {
$tableName = $tableName . '_' . uniqid();
}
$param['seller_id'] = $this->admin['seller_id'];
$param['table'] = $tableName;
$param['code'] = uniqid(); // 表单唯一标识
$param['create_time'] = date('Y-m-d H:i:s');
$diyFormModel->insert($param);
} catch (\Exception $e) {
return jsonReturn(-4, $e->getMessage());
}
return jsonReturn(0, lang('添加成功'));
}
public function edit(DiyForm $diyFormModel)
{
$param = input('post.');
if (empty($param['name'])) {
return jsonReturn(-1, lang('请输入表单名称'));
}
try {
$has = $diyFormModel->where('name', $param['name'])->where('id', '<>', $param['id'])->find();
if (!empty($has)) {
return jsonReturn(-3, lang('该表单已经存在'));
}
$pinyinModel = new Pinyin();
$tableName = $pinyinModel->abbr($param['name']);
$has = $diyFormModel->where('table', $tableName)->where('id', '<>', $param['id'])->find();
if (!empty($has)) {
$tableName = $tableName . '_' . uniqid();
}
$param['table'] = $tableName;
$param['update_time'] = date('Y-m-d H:i:s');
$diyFormModel->where('id', $param['id'])->update($param);
} catch (\Exception $e) {
return jsonReturn(-4, $e->getMessage());
}
return jsonReturn(0, lang('编辑成功'));
}
public function del(DiyForm $diyFormModel)
{
$id = input('param.id');
try {
$info = $diyFormModel->where('id', $id)->find();
if ($info['status'] == 2) {
return jsonReturn(-1, lang('该表单已经发布,请先卸载'));
}
$diyFormModel->where('id', $id)->delete();
} catch (\Exception $e) {
return jsonReturn(-2, $e->getMessage());
}
return jsonReturn(0, lang('删除成功'));
}
public function detail(DiyForm $diyFormModel)
{
$param = input('param.');
try {
$info = $diyFormModel->where('id', $param['id'])->find();
$formJson = json_decode($info['design_content'], true);
$field2Dict = [];
$header = [];
foreach ($formJson as $vo) {
if (!isset($vo['field'])) {
continue;
}
$header[] = [
'label' => $vo['title'],
'property' => $vo['field'],
'type' => $vo['type'],
'options' => $vo['options'] ?? ''
];
// 为了方便字典翻译,优化前端显示
if (isset($vo['options'])) {
$dictDataMap = [];
foreach ($vo['options'] as $k => $v) {
$dictDataMap[$v['value']] = $v['label'];
}
$field2Dict[$vo['field']] = $dictDataMap;
}
if ($vo['type'] == 'switch') {
$dictDataMap = [];
$dictDataMap[$vo['props']['activeValue']] = $vo['props']['activeText'];
$dictDataMap[$vo['props']['inactiveValue']] = $vo['props']['inactiveText'];
$field2Dict[$vo['field']] = $dictDataMap;
}
}
$param['queryParams'] = json_decode($param['queryParams'], true);
if (!empty($param['queryParams']) &&
!empty($param['queryParams']['condition']) &&
!empty($param['queryParams']['childTips'])) {
$where = $this->buildWhere($param['queryParams']['childTips']);
if ($param['queryParams']['condition'] == 'and') {
$data = Db::table(makeFormTable($info['table']))->where($where)
->order('id', 'desc')->paginate($param['limit']);
} else if ($param['queryParams']['condition'] == 'or') {
$data = Db::table(makeFormTable($info['table']))->whereOr($where)
->order('id', 'desc')->paginate($param['limit']);
}
} else {
$data = Db::table(makeFormTable($info['table']))
->order('id', 'desc')->paginate($param['limit']);
}
$data = $data->each(function ($item) use ($field2Dict) {
foreach ($item as $key => $vo) {
if (isset($field2Dict[$key])) {
$showValueMap = [];
$valueMap = explode(',', $vo);
foreach ($valueMap as $valueKey) {
$showValueMap[] = isset($field2Dict[$key][$valueKey]) ? $field2Dict[$key][$valueKey] : $valueKey;
}
$item[$key] = implode(',', $showValueMap);
}
}
return $item;
});
} catch (\Exception $e) {
return jsonReturn(-3, $e->getMessage() . $e->getLine());
}
return jsonReturn(0, 'success', [
'header' => $header,
'data' => $data->getCollection(),
'total' => $data->total()
]);
}
public function deploy(DiyForm $diyFormModel)
{
$id = input('param.id');
$info = $diyFormModel->where('id', $id)->find();
if ($info['status'] == 2) {
return jsonReturn(-1, lang('该表单已经发布,无需再次发布'));
}
if (empty($info['design_content'])) {
return jsonReturn(-2, lang('请先完成表单设计'));
}
$tableName = makeFormTable($info['table']);
$title = $info['name'];
$column = '';
$columnMap = json_decode($info['design_content'], true);
foreach ($columnMap as $key => $vo) {
if (empty($vo['field'])) {
continue;
}
// 强迫症为了dd打印的时候格式对其其实没啥意义
$tab = '';
if ($key > 0) {
$tab = ' ';
}
$column .= $tab . '`' . $vo['field'] . '` varchar(255) NULL DEFAULT NULL COMMENT "' . $vo['title'] . '",' . PHP_EOL;
}
try {
$sql = <<<EOL
CREATE TABLE `{$tableName}` (
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT "id",
{$column} `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT "创建时间",
`update_time` datetime NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT "更新时间",
`visitor_id` varchar(255) DEFAULT '' COMMENT '前端标识',
`sub_id` int(11) DEFAULT 0 COMMENT '关联内容id',
`ip` varchar(20) DEFAULT '',
`user_agent` varchar(255) DEFAULT '' COMMENT '浏览器标识',
PRIMARY KEY (`id`)
)ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 COMMENT='{$title}';
EOL;
Db::execute($sql);
$diyFormModel->where('id', $id)->update([
'status' => 2,
'update_time' => date('Y-m-d H:i:s')
]);
} catch (\Exception $e) {
return jsonReturn(-4, $e->getMessage());
}
return jsonReturn(0, lang('部署成功'));
}
public function undeploy(DiyForm $diyFormModel)
{
$id = input('param.id');
$info = $diyFormModel->where('id', $id)->find();
if ($info['status'] != 2) {
return jsonReturn(-1, lang('该表单尚未发布,无法卸载'));
}
try {
Db::query('DROP TABLE IF EXISTS `' . makeFormTable($info['table']) . '`');
$diyFormModel->where('id', $id)->update([
'status' => 1,
'update_time' => date('Y-m-d H:i:s')
]);
} catch (\Exception $e) {
return jsonReturn(-1, $e->getMessage());
}
return jsonReturn(0, lang('卸载成功'));
}
// 获取问题和答案详情
public function quesDetail(DiyForm $diyFormModel)
{
$param = input('param.');
try {
$info = $diyFormModel->where('id', $param['id'])->find();
$formJson = json_decode($info['design_content'], true);
$field2Dict = [];
$header = [];
foreach ($formJson as $vo) {
if (!isset($vo['field'])) {
continue;
}
$header[$vo['field']] = [
'label' => $vo['title'],
'property' => $vo['field'],
'type' => $vo['type'],
'options' => []
];
if ($vo['type'] == 'switch') {
$header[$vo['field']]['options'][$vo['props']['activeValue']] = ['label' => $vo['props']['activeText']];
$header[$vo['field']]['options'][$vo['props']['inactiveValue']] = ['label' => $vo['props']['inactiveText']];
}
if (!empty($vo['options'])) {
foreach ($vo['options'] as $v) {
$header[$vo['field']]['options'][$v['value']] = $v;
}
}
}
$param['queryParams'] = json_decode($param['queryParams'], true);
if (!empty($param['queryParams']) &&
!empty($param['queryParams']['condition']) &&
!empty($param['queryParams']['childTips'])) {
$where = $this->buildWhere($param['queryParams']['childTips']);
if ($param['queryParams']['condition'] == 'and') {
$data = Db::table(makeFormTable($info['table']))->where($where)->select();
} else if ($param['queryParams']['condition'] == 'or') {
$data = Db::table(makeFormTable($info['table']))->whereOr($where)->select();
} else {
$data = [];
}
} else {
$data = Db::table(makeFormTable($info['table']))->select();
}
$newData = [];
$quesNum = 1;
foreach ($header as $key => $value) {
foreach ($data as $item) {
if (!isset($item[$key])) {
continue;
}
$newData[$key]['quesNum'] = $quesNum;
$newData[$key]['type'] = $value['type'];
$newData[$key]['ques'] = $value['label'];
if (!isset($newData[$key]['total'])) {
$newData[$key]['total'] = 0;
}
if ($value['type'] == 'input' || $value['type'] == 'timePicker' || $value['type'] == 'datePicker') {
$newData[$key]['type_name'] = '填空';
$newData[$key]['type'] = 'list';
// 填空题 ,填的才是答案,使用列表展示
$newData[$key]['total']++;
$newData[$key]['detail'][] = [
'id' => $item['id'],
'answer' => $item[$key],
];
} else if ($value['type'] == 'radio' || $value['type'] == 'select' || $value['type'] == 'switch') {
$newData[$key]['type_name'] = '单选';
$newData[$key]['type'] = 'radio';
// 单选题 使用饼图展示,需要计算百分比
$newData[$key]['total']++;
if (isset($newData[$key]['detail'][$item[$key]]['count'])) {
$newData[$key]['detail'][$item[$key]]['count']++;
} else {
$newData[$key]['detail'][$item[$key]]['answer'] = $value['options'][$item[$key]]['label'];
$newData[$key]['detail'][$item[$key]]['count'] = 1;
}
} else if ($value['type'] == 'slider' || $value['type'] == 'rate'){
$newData[$key]['type_name'] = '滑块/打分';
$newData[$key]['type'] = 'rate';
$newData[$key]['total']++;
if (isset($newData[$key]['detail'][$item[$key]]['count'])) {
$newData[$key]['detail'][$item[$key]]['count']++;
} else {
$newData[$key]['detail'][$item[$key]]['answer'] = $item[$key];
$newData[$key]['detail'][$item[$key]]['count'] = 1;
}
}
else if ($value['type'] == 'checkbox') {
$newData[$key]['type_name'] = '多选';
// 多选题 默认使用柱状图,需要计算百分比
$answer = explode(',', $item[$key]);
foreach ($answer as $v) {
$newData[$key]['total']++;
if (isset($newData[$key]['detail'][$v]['count'])) {
$newData[$key]['detail'][$v]['count']++;
} else {
$newData[$key]['detail'][$v]['answer'] = $value['options'][$v]['label'];
$newData[$key]['detail'][$v]['count'] = 1;
}
}
}
}
$quesNum++;
}
// 整理成前端echart需要的数据
foreach ($newData as &$value) {
if ($value['type'] == 'list') {
continue;
}
$newDetail = [];
foreach ($value['detail'] as $v) {
$newDetail['data'][] = [
'name' => $v['answer'],
'value' => $v['count'],
];
$newDetail['nameArr'][] = $v['answer'];
$newDetail['valueArr'][] = $v['count'];
}
$value['detail'] = $newDetail;
}
// dd($newData);
} catch (\Exception $e) {
return jsonReturn(-3, $e->getMessage() . $e->getLine());
}
return jsonReturn(0, lang('成功'), [
'header' => array_values($header),
'data' => $newData,
'total' => count($newData),
]);
}
private function buildWhere($childTips)
{
$where = [];
foreach ($childTips as $vo) {
switch ($vo['rule']) {
case 'eq':
$where[] = [$vo['field'], '=', $vo['val']];
break;
case 'like':
$where[] = [$vo['field'], 'like', '%' . $vo['val'] . '%'];
break;
case 'left_like':
$where[] = [$vo['field'], 'like', '%' . $vo['val']];
break;
case 'right_like':
$where[] = [$vo['field'], 'like', $vo['val'] . '%'];
break;
case 'neq':
$where[] = [$vo['field'], '<>', $vo['val']];
break;
case 'gt':
$where[] = [$vo['field'], '>', $vo['val']];
break;
case 'gte':
$where[] = [$vo['field'], '>=', $vo['val']];
break;
break;
case 'lt':
$where[] = [$vo['field'], '<', $vo['val']];
break;
case 'lte':
$where[] = [$vo['field'], '<=', $vo['val']];
break;
}
}
return $where;
}
}

View File

@ -0,0 +1,14 @@
<?php
namespace app\controller\backend;
use app\service\GoogleRequest;
class GoogleStatisticsController extends \app\BaseController
{
public function read() {
$googleStatistics = new GoogleRequest();
$googleStatistics->start();
}
}

View File

@ -0,0 +1,154 @@
<?php
declare (strict_types = 1);
namespace app\controller\backend;
use app\exception\ModelException;
use app\model\InnerChart;
use app\validate\InnerChartValidate;
use think\exception\ValidateException;
use think\facade\Lang;
class InnerChartController extends BaseController
{
/**
* 显示资源列表
*
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function index(InnerChart $innerChart): \think\response\Json
{
$limit = $this->setLimit();
$type = input('type');
$siteId = (int)input('website_id');
if(empty($type)){
$type = 1;
}
$where = [
['seller_id' ,'=', $this->admin['seller_id']],
['type' ,'=', $type],
];
if ($type != 2) {
$where[] = ['website_id','=',$siteId];
}
$keyword = input('keyword');
if($keyword){
$where[] = ['keyword','like','%'.$keyword.'%'];
}
try{
$innerList = $innerChart->where($where)->order('id','desc')->paginate($limit)->each(function(&$item){
$count = (int)$item->content()->sum('total');
$item -> count = $count;
$item -> save();
});
}catch(\Exception $e){
throw new ModelException($e->getMessage());
}
return json(['code'=>0,'total'=>$innerList->total(),'msg'=>'success','data'=>$innerList->all()]);
}
/**
* 保存新建的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function save(InnerChart $innerChart): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
$param['seller_id'] = $this->admin['seller_id'];
if(empty($param['website_id'])){
$param['website_id'] = 0;
}
// 数据验证
try{
validate(InnerChartValidate::class)->scene('save')->check($param);
}catch(ValidateException $e){
return jsonReturn(-1, $e->getError());
}
$res = $innerChart -> addInnerChart($param);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 显示指定的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \app\exception\ModelEmptyException
*/
public function read(InnerChart $innerChart): \think\response\Json
{
$id = (int)input('id');
if(!$id){
return jsonReturn(-1,lang('内链ID不能为空'));
}
$where = [
'id' => $id,
'seller_id' => $this->admin['seller_id']
];
$res = $innerChart->getInnerChart($where);
return json($res);
}
/**
* 保存更新的资源
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function update(InnerChart $innerChart): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
try {
validate(InnerChartValidate::class)->scene('update')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
if(empty($param['website_id'])){
$param['website_id'] = 0;
}
$where = [
'id' => $param['id'],
'seller_id' => $this->admin['seller_id'],
];
$res = $innerChart -> updateInnerChart($where,$param);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 删除指定资源
*
* @throws \app\exception\ModelException
*/
public function delete(InnerChart $innerChart): \think\response\Json
{
if(request()->isPost()){
$id = (int)input('id');
if(!$id){
return jsonReturn(-1,lang('内链ID不能为空'));
}
$where = [
'id' => $id,
'seller_id' => $this->admin['seller_id']
];
$res = $innerChart->delInnerChart($where);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
}

View File

@ -0,0 +1,130 @@
<?php
namespace app\controller\backend;
use \app\validate\InquiryCategoryValidate;
use app\model\InquiryCategory;
use think\exception\ValidateException;
use \think\response\Json;
class InquiryCategoryController extends BaseController
{
/**
* 获取线索列表,可按条件查询
* @return Json
*/
public function index(): Json
{
if (request()->isGet()) {
$param['seller_id'] = $this->admin['seller_id'];
$inquiryCategory = new InquiryCategory();
$res = $inquiryCategory->getInquiryCategoryList($param);
return json($res);
}
return jsonReturn(-2, lang('请求方法错误'));
}
/**
* 询单类别查询
* @return Json
*/
public function read(): Json
{
if (request()->isGet()) {
$param = input('get.');
$param['seller_id'] = $this->admin['seller_id'];
try {
validate(InquiryCategoryValidate::class)->scene('read')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-4, $e->getMessage());
}
$inquiryCategory = new InquiryCategory();
$res = $inquiryCategory->getInquiryCategory($param);
return json($res);
}
return jsonReturn(-2, lang('请求方法错误'));
}
/**
* 新增询单类别
* @return Json
*/
public function save(): Json
{
if (request()->isPost()) {
$param = input('post.');
$param['seller_id'] = $this->admin['seller_id'];
try {
validate(InquiryCategoryValidate::class)->scene('save')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-4, $e->getMessage());
}
$inquiryCategory = new InquiryCategory();
$res = $inquiryCategory->addInquiryCategory($param);
return json($res);
}
return jsonReturn(-2, lang('请求方法错误'));
}
/**
* 更新询单类别
* @return Json
*/
public function update(): Json
{
if (request()->isPost()) {
$param = input('post.');
$param['seller_id'] = $this->admin['seller_id'];
try {
validate(InquiryCategoryValidate::class)->scene('update')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-4, $e->getMessage());
}
$inquiryCategory = new InquiryCategory();
$res = $inquiryCategory->updateInquiryCategory($param);
return json($res);
}
return jsonReturn(-2, lang('请求方法错误'));
}
/**
* 删除询单类别
* @return Json
*/
public function delete(): Json
{
if (request()->isPost()) {
$param = input('post.');
$param['seller_id'] = $this->admin['seller_id'];
try {
validate(InquiryCategoryValidate::class)->scene('read')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-4, $e->getMessage());
}
$inquiryCategory = new InquiryCategory();
$res = $inquiryCategory->deleteInquiryCategory($param);
return jsonReturn($res['code'], $res['msg']);
}
return jsonReturn(-2, lang('请求方法错误'));
}
}

View File

@ -0,0 +1,183 @@
<?php
namespace app\controller\backend;
use app\model\Inquiry;
use app\model\RecycleBin;
use app\validate\InquiryValidate;
use think\exception\ValidateException;
use think\facade\Lang;
use think\response\Json;
class InquiryController extends BaseController {
/**
* 查询询单列表
* @return Json
*/
public function index(): Json
{
if (request()->isGet()) {
$siteId = (int)input('website_id');
if(empty($siteId)){
return jsonReturn(-1,Lang::get('网站ID不能为空'));
}
$where = [
'seller_id' => $this->admin['seller_id'],
'website_id' => $siteId,
'is_del' => 1,
];
$limit = $this->setLimit();
$inquiry = new Inquiry();
$res = $inquiry->getInquiryList($where,'*', $limit);
return json(pageReturn($res));
}
return jsonReturn(-2, lang('请求方法错误'));
}
/**
* 新增询单信息
* @return Json
* @method Post
*/
public function backendSave(): Json
{
if(request()->isPost()){
$param = input('post.');
$param['seller_id'] = $this->admin['seller_id'];
if (empty($param['phone']) && empty($param['wechat']) && empty($param['email']) && empty($param['qq']) && empty($param['telphone'])) {
return jsonReturn(-4, lang('联系方式必须填写一个'));
}
try {
validate(InquiryValidate::class)->scene('save')->check($param);
} catch (ValidateException $e){
return jsonReturn(-4, $e->getMessage());
}
$inquiry = new Inquiry();
$res = $inquiry -> addInquiry($param);
return json($res);
}
return jsonReturn(-2, lang('请求方法错误'));
}
/**
* 询单信息查询
* @return Json
* @method Post
*/
public function read(): Json
{
if (request()->isGet()) {
$param = input('get.');
try {
validate(InquiryValidate::class)->scene('read')->check($param);
} catch (ValidateException $e){
return jsonReturn(-4, $e->getMessage());
}
$param['seller_id'] = $this->admin['seller_id'];
$inquiry = new Inquiry();
$res = $inquiry->getInquiry($param);
return json($res);
}
return jsonReturn(-2, lang('请求方法错误'));
}
/**
* 更新询单信息
* @return Json
* @method Post
*/
public function update(): Json
{
if(request()->isPost()) {
$param = input('post.');
$param['seller_id'] = $this->admin['seller_id'];
if (empty($param['mobile']) && empty($param['wechat']) && empty($param['email']) && empty($param['qq'])) {
return jsonReturn(-4, lang('联系方式必须填写一个'));
}
try {
validate(InquiryValidate::class)->scene('update')->check($param);
} catch (ValidateException $e){
return jsonReturn(-4, $e->getMessage());
}
$inquiry = new Inquiry();
$res = $inquiry->updateInquiry($param);
return json($res);
}
return jsonReturn(-2, lang('请求方法错误'));
}
/**
* 删除询单信息
* @return Json
* @throws \app\exception\ModelException
*/
public function delete(): Json
{
if (request()->isPost()) {
$param = input('post.');
$param['seller_id'] = $this->admin['seller_id'];
try {
validate(InquiryValidate::class)->scene('read')->check($param);
} catch (ValidateException $e){
return jsonReturn(-4, $e->getMessage());
}
$Inquiry = new Inquiry();
$inquiry = $Inquiry->getInquiry($param)['data'];
if(empty($inquiry)){
jsonReturn(-3, lang('询盘不存在'));
}
// 复制删除内容到回收站表
$recycleBin = new RecycleBin();
$binData = [
'seller_id' => $param['seller_id'],
'object_id' => $inquiry['id'],
'sub_id' => 0,
'module_id' => 0,
'table_name' => lang('询盘'),
'title' => !empty($inquiry['title']) ? $inquiry['title'] : lang('询盘内容') . $inquiry['id'],
'admin_id' => $this->admin['uid'],
'name' => $this->admin['name'],
];
$recycleBin -> addRecycleBin($binData);
$inquiry->is_del = 2;
$inquiry->delete_time = time();
$inquiry->save();
return jsonReturn(0, lang('删除成功'));
}
return jsonReturn(-2, lang('请求方法错误'));
}
public function batch_delete(): Json
{
if (request()->isPost()) {
$param = input('post.');
try {
validate(InquiryValidate::class)->scene('batch')->check($param);
} catch (\Exception $e) {
return jsonReturn(-4, $e->getMessage());
}
$where = [
['seller_id' ,'=', $this->admin['seller_id']],
['ids','in', $param['ids']],
];
$linkWebsite = new Inquiry();
$res = $linkWebsite->batchDeleteInquiry($where);
return jsonReturn($res['code'], $res['msg']);
}
return jsonReturn(-1, lang('请求方法错误'));
}
}

View File

@ -0,0 +1,152 @@
<?php
namespace app\controller\backend;
use app\model\InquiryEmail;
use app\model\WebsiteInquiryEmail;
use app\validate\InquiryEmailValidate;
use think\facade\Db;
use think\facade\Lang;
use \think\response\Json;
class InquiryEmailController extends BaseController
{
/**
* @param InquiryEmail $InquiryEmail
* @return Json
* @throws \think\db\exception\DbException
*/
public function index (InquiryEmail $InquiryEmail): Json
{
if (request()->isGet()) {
$siteId = (int)input('website_id');
if(!$siteId){
return jsonReturn(-1,Lang::get('网站ID不能为空'));
}
$limit = $this->setLimit();
$where = [
'seller_id' => $this->admin['seller_id'],
'website_id' => $siteId,
];
$WebsiteInquiryEmail = new WebsiteInquiryEmail();
$ids = $WebsiteInquiryEmail->where($where)->column('inquiry_email_id');
if(empty($ids)){
return json(['code' => 0, 'msg' => 'ok', 'count' => 0, 'data' => []]);
}
$emailWhere = [
['seller_id','=',$this->admin['seller_id']],
['id','in',$ids]
];
$inquiryEmail = $InquiryEmail -> where($emailWhere)->paginate($limit)->each(function(&$item)use($WebsiteInquiryEmail){
$ids = $WebsiteInquiryEmail->where($where = [
'seller_id' => $this->admin['seller_id'],
'inquiry_email_id' => $item['id'],
])->column('website_id');
$item['website_id'] = $ids;
});
return json(['code' => 0, 'msg' => 'ok', 'count' => $inquiryEmail->total(), 'data' => $inquiryEmail->all()]);
}
return jsonReturn(-2, lang('请求方法错误'));
}
/**
* @return Json
*/
public function save(InquiryEmail $InquiryEmail): Json
{
if (request()->isPost()) {
$param = input('post.');
try {
validate(InquiryEmailValidate::class)->scene('save')->check($param);
} catch (\Exception $e) {
return jsonReturn(-4, $e->getMessage());
}
$param['seller_id'] = $this->admin['seller_id'];
$siteIds = $param['website_id'];
unset($param['website_id']);
Db::startTrans();
try{
$res = $InquiryEmail -> addInquiryEmail($param);
$inquiryEmail = $res['data'];
$inquiryEmail->website()->attach($siteIds,['seller_id'=>$param['seller_id']]);
Db::commit();
}catch (\Exception $e){
Db::rollback();
return jsonReturn(0,lang('系统错误请重试'));
}
return json($res);
}
return jsonReturn(-2, lang('请求方法错误'));
}
/**
* @return Json
*/
public function update(InquiryEmail $InquiryEmail): Json
{
if (request()->isPost()) {
$param = input('post.');
try {
validate(InquiryEmailValidate::class)->scene('update')->check($param);
} catch (\Exception $e) {
return jsonReturn(-4, $e->getMessage());
}
Db::startTrans();
try{
$param['seller_id'] = $this->admin['seller_id'];
$inquiryEmail = $InquiryEmail->getInquiryEmail(['id'=>$param['id'],'seller_id'=>$param['seller_id']])['data'];
$inquiryEmail->website()->detach();
$inquiryEmail->website()->attach($param['website_id'],['seller_id'=>$param['seller_id']]);
$inquiryEmail->email = $param['email'];
if(isset($param['email'])){
$inquiryEmail->status = $param['status'];
}
$inquiryEmail->save();
Db::commit();
}catch (\Exception $e){
Db::rollback();
return jsonReturn(-1,lang('系统错误请重试'));
}
return jsonReturn(0,lang('保存成功'));
}
return jsonReturn(-2, lang('请求方法错误'));
}
/**
* @return Json
*/
public function delete(InquiryEmail $InquiryEmail): Json
{
if (request()->isPost()) {
$param = input('post.');
$param['seller_id'] = $this->admin['seller_id'];
try {
validate(InquiryEmailValidate::class)->scene('read')->check($param);
} catch (\Exception $e) {
return jsonReturn(-4, $e->getMessage());
}
Db::startTrans();
try{
$inquiryEmail = $InquiryEmail->getInquiryEmail(['id'=>$param['id'],'seller_id'=>$param['seller_id']])['data'];
$inquiryEmail->website()->detach();
$inquiryEmail->delete();
Db::commit();
}catch (\Exception $e){
Db::rollback();
return jsonReturn(0,lang('系统错误请重试'));
}
return jsonReturn(0, lang('删除成功'));
}
return jsonReturn(-2, lang('请求方法错误'));
}
}

View File

@ -0,0 +1,153 @@
<?php
declare (strict_types = 1);
namespace app\controller\backend;
use app\model\Job;
use app\model\JobCate;
use app\validate\JobCateValidate;
use think\exception\ValidateException;
use think\facade\Lang;
class JobCateController extends BaseController
{
/**
* 显示资源列表
*
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function index(JobCate $jobCate): \think\response\Json
{
$where = [
'seller_id' => $this->admin['seller_id'],
'is_del' => 1,
];
$limit = 10;
$limitParam = (int)input('limit');
if($limitParam){
$limit = $limitParam;
}
// TODO
// 添加其他逻辑
$jobCateList = $jobCate->getJobCateList($where,$limit);
return json(pageReturn($jobCateList));
}
/**
* 保存新建的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function save(JobCate $jobCate): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
$param['seller_id'] = $this->admin['seller_id'];
// 数据验证
try{
validate(JobCateValidate::class)->scene('save')->check($param);
}catch(ValidateException $e){
return jsonReturn(-1, $e->getError());
}
// TODO
// 其他逻辑
$res = $jobCate -> addJobCate($param);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 显示指定的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \app\exception\ModelEmptyException
*/
public function read(JobCate $jobCate): \think\response\Json
{
$id = (int)input('id');
if(!$id){
// TODO
// 修改错误消息
return jsonReturn(-1,'ErrorMsg');
}
$where = [
'id' => $id,
'seller_id' => $this->admin['seller_id']
];
// TODO
// 其他逻辑
$res = $jobCate->getJobCate($where);
return json($res);
}
/**
* 保存更新的资源
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function update(JobCate $jobCate): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
try {
validate(JobCateValidate::class)->scene('update')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
$where = [
'id' => $param['id'],
'seller_id' => $this->admin['seller_id'],
'is_del' => 1,
];
$res = $jobCate -> updateJobCate($where,$param);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 删除指定资源
*
* @throws \app\exception\ModelException
*/
public function delete(JobCate $jobCate, Job $job): \think\response\Json
{
if(request()->isPost()){
$id = (int)input('id');
$seller_id = $this->admin['seller_id'];
if(!$id){
// TODO 替换错误提示
return jsonReturn(-1,'ErrorMsg');
}
// TODO 分类使用判断
$job_where = [
'job_cate_id' => $id,
'seller_id' => $seller_id,
];
$has = $job->getJob($job_where);
if ($has['code'] == 0) {
return jsonReturn(-3, lang('工作类别在使用中'));
} else {
$where = [
'id' => $id,
'seller_id' => $seller_id,
];
$res = $jobCate->softDelJobCate($where);
return json($res);
}
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
}

View File

@ -0,0 +1,154 @@
<?php
declare (strict_types = 1);
namespace app\controller\backend;
use app\model\Job;
use app\model\JobCity;
use app\validate\JobCityValidate;
use think\exception\ValidateException;
use think\facade\Lang;
class JobCityController extends BaseController
{
/**
* 显示资源列表
*
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function index(JobCity $jobCity): \think\response\Json
{
$where = [
'seller_id' => $this->admin['seller_id'],
'is_del' => 1,
];
$limit = 10;
$limitParam = (int)input('limit');
if($limitParam){
$limit = $limitParam;
}
// TODO
// 添加其他逻辑
$jobCityList = $jobCity->getJobCityList($where,$limit);
return json(pageReturn($jobCityList));
}
/**
* 保存新建的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function save(JobCity $jobCity): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
$param['seller_id'] = $this->admin['seller_id'];
// 数据验证
try{
validate(JobCityValidate::class)->scene('save')->check($param);
}catch(ValidateException $e){
return jsonReturn(-1, $e->getError());
}
// TODO
// 其他逻辑
$res = $jobCity -> addJobCity($param);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 显示指定的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \app\exception\ModelEmptyException
*/
public function read(JobCity $jobCity): \think\response\Json
{
$id = (int)input('id');
if(!$id){
// TODO
// 修改错误消息
return jsonReturn(-1,'ErrorMsg');
}
$where = [
'id' => $id,
'seller_id' => $this->admin['seller_id']
];
// TODO
// 其他逻辑
$res = $jobCity->getJobCity($where);
return json($res);
}
/**
* 保存更新的资源
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function update(JobCity $jobCity): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
try {
validate(JobCityValidate::class)->scene('update')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
$where = [
'id' => $param['id'],
'seller_id' => $this->admin['seller_id'],
'is_del' => 1,
];
$res = $jobCity -> updateJobCity($where,$param);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 删除指定资源
*
* @throws \app\exception\ModelException
*/
public function delete(JobCity $jobCity, Job $job): \think\response\Json
{
if(request()->isPost()){
$id = (int)input('id');
$seller_id = $this->admin['seller_id'];
if(!$id){
// TO DO
// 替换错误提示
return jsonReturn(-1,'ErrorMsg');
}
// TODO 分类使用判断
$job_where = [
'job_city_id' => $id,
'seller_id' => $seller_id,
];
$has = $job->getJob($job_where);
if ($has['code'] == 0) {
return jsonReturn(-3, lang('工作类别在使用中'));
} else {
$where = [
'id' => $id,
'seller_id' => $this->admin['seller_id']
];
$res = $jobCity->softDelJobCity($where);
return json($res);
}
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
}

View File

@ -0,0 +1,164 @@
<?php
declare (strict_types = 1);
namespace app\controller\backend;
use app\exception\ModelEmptyException;
use app\exception\ModelException;
use app\model\Job;
use app\model\JobCate;
use app\model\JobCity;
use app\model\Website;
use app\validate\JobValidate;
use think\exception\ValidateException;
use think\facade\Lang;
use think\response\Json;
class JobController extends BaseController
{
/**
* 显示资源列表
*
* @return Json
* @throws ModelException
*/
public function index(Job $job): Json
{
$seller_id = $this->admin['seller_id'];
$job_city_id = input('job_city_id');
$job_cate_id = input('job_cate_id');
$title = input('title');
$where = [
'seller_id' => $seller_id,
'is_del' => 1,
];
$limit = 10;
$limitParam = (int)input('limit');
if($limitParam){
$limit = $limitParam;
}
// TODO
// 添加其他逻辑
if (!empty($job_city_id)) {
$where['job_city_id'] = $job_city_id;
}
if (!empty($job_cate_id)) {
$where['job_cate_id'] = $job_cate_id;
}
if (!empty($title)) {
// 模糊查询
$jobList = $job->searchJob($where,$title, $limit);
} else {
$jobList = $job->getJobList($where,$limit);
}
return json(pageReturn($jobList));
}
/**
* 保存新建的资源
*
* @return Json
* @throws ModelException
*/
public function save(Job $job): Json
{
if(request()->isPost()){
$param = input('post.');
$param['seller_id'] = $this->admin['seller_id'];
// 数据验证
try{
validate(JobValidate::class)->scene('save')->check($param);
}catch(ValidateException $e){
return jsonReturn(-1, $e->getError());
}
$res = $job -> addJob($param);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 显示指定的资源
*
* @return Json
* @throws ModelException
* @throws ModelEmptyException
*/
public function read(Job $job): Json
{
$id = (int)input('id');
if(!$id){
return jsonReturn(-1,lang('不能为空'));
}
$siteId = (int)input('website_id');
$where = [
'id' => $id,
'seller_id' => $this->admin['seller_id'],
'website_id' => $siteId,
];
$jobRes = $job->getJob($where,['jobCity','jobCity'])['data']->toArray();
return jsonReturn(0, lang('查询成功'), $jobRes);
}
/**
* 保存更新的资源
* @return Json
* @throws ModelException
*/
public function update(Job $job): Json
{
if(request()->isPost()){
$param = input('post.');
$param['seller_id'] = $this->admin['seller_id'];
try {
validate(JobValidate::class)->scene('update')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
$where = [
'id' => $param['id'],
'seller_id' => $this->admin['seller_id'],
'is_del' => 1,
'website_id' => $param['website_id']
];
$res = $job -> updateJob($where,$param);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 删除指定资源
*
* @throws ModelException
*/
public function delete(Job $job): Json
{
if(request()->isPost()){
$id = (int)input('id');
if(!$id){
return jsonReturn(-1,lang('职位ID不能为空'));
}
$siteId = (int)input('website_id');
$where = [
'id' => $id,
'seller_id' => $this->admin['seller_id'],
'website_id' => $siteId,
];
$res = $job->delJob($where);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
}

View File

@ -0,0 +1,356 @@
<?php
namespace app\controller\backend;
use app\model\Keyword;
use app\model\KeywordQuery;
use app\model\Website;
use app\service\ExcelService;
use app\service\KeywordService;
use app\service\MonitorService;
use app\validate\KeywordValidate;
use think\exception\ValidateException;
use think\facade\Lang;
use think\response\Json;
class KeywordController extends BaseController
{
public $header = ['关键词名称','关键词链接','关键词位置','关键词状态'];
public $map = ['name','url','position','status'];
/**
* 读取数据列表
* @return Json
* @method Post
* @error_number 0: 成功, -1: 数据库查询失败, -2: 请求方法错误, -3: 数据重复, -4:数据缺少或校验不通过,
*/
public function index(Keyword $Keyword): Json
{
if (request()->isGet()) {
$websiteId = (int)input('website_id', '', 'trim');
if(!$websiteId){
return jsonReturn(-1,Lang::get('站点不能为空'));
}
$where = [
'website_id' => $websiteId,
'seller_id' => $this->admin['seller_id'],
];
$limit = $this->setLimit();
$res = $Keyword->getKeywordList($where, $limit,'id,website_id,name,url,position,sort,baidu_pc,baidu_mob,three_pc,sougou_mob,status',['website']);
return json(pageReturn($res));
}
return jsonReturn(-2, Lang::get('请求方法错误'));
}
/**
* 读取数据
* @return Json
*/
public function read(Keyword $Keyword)
{
if (request()->isGet()) {
$param = input('get.');
try {
validate(KeywordValidate::class)->scene('read')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-4, $e->getMessage());
}
$param['seller_id'] = $this->admin['seller_id'];
$res = $Keyword->getKeyword($param);
return json($res);
}
return jsonReturn(-2, Lang::get('请求方法错误'));
}
/**
* 保存
* @param Keyword $Keyword
* @return Json
* @throws \app\exception\ModelException
* @throws \app\exception\ModelNotUniqueException
*/
public function save(Keyword $Keyword): Json
{
if (request()->isPost()) {
$param = input('post.');
try {
validate(KeywordValidate::class)->scene('save')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-4, $e->getMessage());
}
$param['seller_id'] = $this->admin['seller_id'];
$uniqueWhere = [
'website_id' => $param['website_id'],
'name' => $param['name'],
'url' => $param['url']
];
$Keyword->saveUnique($uniqueWhere,Lang::get('关键词已经存在'));
$res = $Keyword->addKeyword($param);
return json($res);
}
return jsonReturn(-2, Lang::get('请求方法错误'));
}
/**
* 编辑
* @param Keyword $Keyword
* @return Json
* @throws \app\exception\ModelException
* @throws \app\exception\ModelNotUniqueException
*/
public function update(Keyword $Keyword): Json
{
if (request()->isPost()) {
$param = input('post.');
try {
validate(KeywordValidate::class)->scene('update')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-4, $e->getMessage());
}
$uniqueWhere = [
'website_id' => $param['website_id'],
'name' => $param['name'],
'url' => $param['url']
];
$Keyword->updateUnique($uniqueWhere,$param['id'],Lang::get('关键词已经存在'));
$updateWhere = [
'id' => $param['id'],
'seller_id' => $this->admin['seller_id'],
'website_id' => $param['website_id'],
];
$res = $Keyword->updateKeyword($updateWhere,$param);
return json($res);
}
return jsonReturn(-2, Lang::get('请求方法错误'));
}
/**
* 删除数据
* @return Json
*/
public function delete(Keyword $Keyword): Json
{
if (request()->isPost()) {
$param = input('post.');
try {
validate(KeywordValidate::class)->scene('delete')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-4, $e->getMessage());
}
if(empty($param['keyword_id'])){
jsonReturn(0, lang('删除成功'));
}else{
if(count($param['keyword_id'])){
$where = [
'id' => $param['keyword_id'][0],
'seller_id' => $this->admin['seller_id'],
'website_id' => $param['website_id']
];
}else{
$where = [
['id' , 'in',$param['keyword_id']],
['seller_id' ,'=', $param['seller_id']],
['website_id', '=', $param['website_id']]
];
}
$res = $Keyword->deleteKeyword($where);
return json($res);
}
}
return jsonReturn(-2, Lang::get('请求方法错误'));
}
/**
* 导入关键词
* @param ExcelService $excelService
* @param Keyword $Keyword
* @return Json
* @throws \PhpOffice\PhpSpreadsheet\Exception
* @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
*/
public function import(ExcelService $excelService,Keyword $Keyword): Json
{
$param = input('post.');
$param['file'] = request()->file('file');
try {
validate(KeywordValidate::class)->scene('import')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-4, $e->getMessage());
}
$data = $excelService->readData($param['file']);
if(empty($data)){
return jsonReturn(-1,Lang::get('数据读取失败或者文件加为空,请检查'));
}
$data = $this->checkData($data);
if(isset($data['code'])){
return json($data);
}
$data = $this->dealWithData($data,$param['website_id']);
$res = $Keyword->addAllCustomData($data);
return json($res);
}
/**
* @param $data
* @return array
*/
public function checkData($data): array
{
$header = array_shift($data);
if(count($header) != 4){
return dataReturn(-2,Lang::get('数据格式错误,请参考样例'));
}
$i = 0;
foreach($header as $value){
if($value != $this->header[$i]){
return dataReturn(-3,Lang::get('数据格式错误,请参考样例'));
}
$i++;
}
return $data;
}
/**
* @param $data
* @param $siteId
* @return array
*/
public function dealWithData($data,$siteId): array
{
$tmpData = [];
foreach($data as $val){
if(!empty($val)){
$tmp = ['website_id' => $siteId];
$tmp['seller_id'] = $this->admin['seller_id'];
$i = 0;
foreach($val as $vv){
if($i == 0 && empty($vv)){
break;
}
if($i == 3){
if($vv == '正常'){
$vv = 1;
}else{
$vv = 2;
}
}
$tmp[$this->map[$i]] = $vv;
if($i == 3){
array_push($tmpData,$tmp);
}
$i++;
}
}
}
return $tmpData;
}
/**
* 关键词模版
* @return Json
*/
public function template(): Json
{
return jsonReturn(0,'success',config('system.keyword_template_path'));
}
/**
* @throws \app\exception\ModelEmptyException
* @throws \app\exception\ModelException
*/
public function monitor(MonitorService $monitorService): Json
{
$param = input('get.');
try {
validate(KeywordValidate::class)->scene('monitor')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-4, $e->getMessage());
}
$param['seller_id'] = $this->admin['seller_id'];
return $monitorService->getMonitorData($param);
}
public function echarts()
{
$param = $this->request->param();
try {
validate(KeywordValidate::class)->scene('echarts')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-4, $e->getMessage());
}
$keywordQueryModel = new KeywordQuery();
$startTime = time() - ($param['days'] * 86400);
$list = $keywordQueryModel->where('create_time', '>=', $startTime)
->where('search_engine', $param['search_engine'])
->where('keyword_id', $param['id'])->select();
$list = $list ? $list->toArray() : [];
$days = [];
$values = [];
foreach ($list as $value) {
$days[] = $value['create_time'];
$values[] = $value['top_rank'];
}
$data = [
'days' => array_values($days),
'values' => $values,
];
return jsonReturn(0, 'success', $data);
}
// 单个更新排名
public function updateRank()
{
$param = $this->request->param();
try {
validate(KeywordValidate::class)->scene('updateRank')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-4, $e->getMessage());
}
$token = KeywordService::getChinaZToken($this->admin['seller_id']);
$keywordModel = new Keyword();
$keywordData = $keywordModel->where([
['id', '=', $param['id']]
])->findOrEmpty();
if (!isset($keywordData['name'])) {
return jsonReturn(-2, Lang::get('关键词不存在!'));
}
switch ($param['search_engine']) {
case 'baidu_pc' :
KeywordService::baiduPcRank($token, $keywordData);
break;
case 'baidu_mob':
KeywordService::baiduMobileRank($token, $keywordData);
break;
case 'three_pc':
KeywordService::pc360Rank($token, $keywordData);
break;
case 'sougou_mob':
KeywordService::sougouMobileRank($token, $keywordData);
break;
default:
return jsonReturn(-3, Lang::get('搜索引擎不存在'));
break;
}
return jsonReturn(0, Lang::get('执行成功!'));
}
}

View File

@ -0,0 +1,91 @@
<?php
declare (strict_types = 1);
namespace app\controller\backend;
use app\model\KeywordQuery;
use app\service\MonitorService;
use app\validate\KeywordQueryValidate;
use think\exception\ValidateException;
use think\facade\Lang;
class KeywordQueryController extends BaseController
{
/**
* 显示资源列表
*
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function index(MonitorService $monitorService): \think\response\Json
{
$param = input('get.');
try {
validate(KeywordQueryValidate::class)->scene('monitor')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-4, $e->getMessage());
}
$param['seller_id'] = $this->admin['seller_id'];
return $monitorService->keywordMonitor($param);
}
/**
* 显示指定的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \app\exception\ModelEmptyException
*/
public function read(KeywordQuery $keywordQuery): \think\response\Json
{
$id = (int)input('id');
if(!$id){
return jsonReturn(-1,Lang::get('关键词ID'));
}
$where = [
'id' => $id,
'seller_id' => $this->admin['seller_id']
];
// TODO
// 其他逻辑
$res = $keywordQuery->getKeywordQuery($where);
return json($res);
}
public function save(KeywordQuery $keywordQuery)
{
$param = input('keywordmonitor');
foreach ( $param as &$val) {
$val['ranks'] = json_encode($val['ranks']);
}
unset($val);
$res = $keywordQuery->saveAll($param);
dd($res);
}
//获取关键词查询列表
public function list()
{
$param = $this->request->param();
$param['limit'] = $param['limit'] ?? 10;
if (empty($param['keyword_id']) || empty($param['engine'])) {
return jsonReturn(-1, Lang::get('参数错误'));
}
$keywordQueryModel = new KeywordQuery();
$list = $keywordQueryModel->getKeywordQueryList([
['keyword_id', '=', $param['keyword_id']],
['search_engine', '=', $param['engine']]
], intval($param['limit']));
return json(pageReturn($list));
}
}

View File

@ -0,0 +1,129 @@
<?php
namespace app\controller\backend;
use app\model\KeywordWebsite;
use app\validate\KeywordWebsiteValidate;
use think\exception\ValidateException;
use think\facade\Lang;
use think\response\Json;
class KeywordWebsiteController extends BaseController
{
/**
* 读取数据列表
* @return Json
* @method Post
* @error_number 0: 成功, -1: 数据库查询失败, -2: 请求方法错误, -3: 数据重复, -4:数据缺少或校验不通过,
*/
public function index(): Json
{
if (\request()->isGet()) {
$param = input('get.');
$param['seller_id'] = $this->admin['seller_id'];
$keyword = new KeywordWebsite();
$res = $keyword->getKeywordWebsiteList($param);
return json($res);
}
return jsonReturn(-2, Lang::get('请求方法错误'));
}
/**
* 读取数据
* @return Json
*/
public function read()
{
if (request()->isGet()) {
$param = input('get.');
$param['seller_id'] = $this->admin['seller_id'];
try {
validate(KeywordWebsiteValidate::class)->scene('read')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-4, $e->getMessage());
}
$KeywordWebsite = new KeywordWebsite();
$res = $KeywordWebsite->getKeywordWebsite($param);
return json($res);
}
return jsonReturn(-2, Lang::get('请求方法错误'));
}
/**
* 存入数据
* @return Json
*/
public function save()
{
if (request()->isPost()) {
$param = input('post.');
$param['seller_id'] = $this->admin['seller_id'];
try {
validate(KeywordWebsiteValidate::class)->scene('save')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-4, $e->getMessage());
}
$KeywordWebsite = new KeywordWebsite();
$res = $KeywordWebsite->addKeywordWebsite($param);
return jsonReturn($res['code'], $res['msg'], $res['data']);
}
return jsonReturn(-2, Lang::get('请求方法错误'));
}
/**
* 更新数据
* @return Json
*/
public function update(): Json
{
if (request()->isPost()) {
$param = input('post.');
$param['seller_id'] = $this->admin['seller_id'];
try {
validate(KeywordWebsiteValidate::class)->scene('update')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-4, $e->getMessage());
}
$KeywordWebsite = new KeywordWebsite();
$res = $KeywordWebsite->updateKeywordWebsite($param);
return jsonReturn($res['code'], $res['msg']);
}
return jsonReturn(-2, Lang::get('请求方法错误'));
}
/**
* 删除数据
* @return Json
*/
public function delete()
{
if (request()->isPost()) {
$param = input('post.');
$param['seller_id'] = $this->admin['seller_id'];
try {
validate(KeywordWebsiteValidate::class)->scene('read')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-4, $e->getMessage());
}
$KeywordWebsite = new KeywordWebsite();
$res = $KeywordWebsite->deleteKeywordWebsite($param);
return jsonReturn($res['code'], $res['msg']);
}
return jsonReturn(-2, Lang::get('请求方法错误'));
}
}

View File

@ -0,0 +1,255 @@
<?php
namespace app\controller\backend;
use app\exception\ModelEmptyException;
use app\exception\ModelException;
use app\model\Link;
use app\model\LinkWebsite;
use app\model\Model;
use app\model\RecycleBin;
use app\service\CacheService;
use app\validate\LinkValidate;
use think\facade\Cache;
use think\facade\Db;
use think\facade\Lang;
use think\response\Json;
class LinkController extends BaseController
{
/**
* 友情链接列表
* @key id seller_id
* @return Json
* @throws \app\exception\ModelException
*/
public function index(Link $link): Json
{
if(request()->isGet()) {
$param = input('get.');
$param['seller_id'] = $this->admin['seller_id'];
try {
validate(LinkValidate::class)->scene('index')->check($param);
} catch (\Exception $e) {
return jsonReturn(-4, $e->getMessage());
}
$where = [
'website_id' => $param['website_id'],
'seller_id' => $param['seller_id'],
'type' => $param['type'],
'is_del' => 1
];
$limit = $this->setLimit();
// 翻页数据
$res = $link->getLinkList($where,$limit);
$data = pageReturn($res);
// 统计数据
$total = $data['count'];
$changeNum = 0;
if($total){
$now = mktime(0,0,0,date('m'),1,date('Y'));
// 上月总数
$lastMonthNum = $link->where($where)->whereTime('create_time','<',$now)->count();
$changeNum = $total - $lastMonthNum;
}
$data['change'] = $changeNum;
return json($data);
}
return jsonReturn(-2, Lang::get('请求方法错误'));
}
/**
* 读取单条友情链接信息
* @return Json
*/
public function read(Link $link): Json
{
if (request()->isGet()) {
$param = input('get.');
$param['seller_id'] = $this->admin['seller_id'];
try {
validate(LinkValidate::class)->scene('read')->check($param);
} catch (\Exception $e) {
return jsonReturn(-4, $e->getMessage());
}
$param['is_del'] = 1;
$res = $link->getLink($param);
return json($res);
}
return jsonReturn(-2, Lang::get('请求方法错误'));
}
/**
* 新增友情链接
* @return Json
* @throws \app\exception\ModelException
* @throws \app\exception\ModelNotUniqueException
*/
public function save(Link $link): Json
{
if(request()->isPost()) {
$param = input('post.');
$param['seller_id'] = $this->admin['seller_id'];
try {
validate(LinkValidate::class)->scene('save')->check($param);
} catch (\Exception $e) {
return jsonReturn(-4, $e->getMessage());
}
if(isset($param['add_time']) && empty($param['add_time'])){
unset($param['add_time']);
}
if(isset($param['end_time']) && empty($param['end_time'])){
unset($param['end_time']);
}
$link->saveUnique(['seller_id'=>$param['seller_id'],'website_id'=>$param['website_id'],'type'=>$param['type'],'name'=>$param['name'],'is_del'=>1],'友情链接已经存在');
$res = $link->addLink($param);
CacheService::deleteCacheList('Link_cache_list');
return json($res);
}
return jsonReturn(-2, Lang::get('请求方法错误'));
}
/**
* 更新友情链接信息
* @return Json
* @throws \app\exception\ModelException
* @throws \app\exception\ModelNotUniqueException
*/
public function update(Link $link): Json
{
if (request()->isPost())
{
$param = input('post.');
try {
validate(LinkValidate::class)->scene('update')->check($param);
} catch (\Exception $e) {
return jsonReturn(-4, $e->getMessage());
}
$where = [
'id' => $param['id'],
'seller_id' => $this->admin['seller_id'],
'website_id' => $param['website_id'],
];
if(isset($param['add_time']) && empty($param['add_time'])){
unset($param['add_time']);
}
if(isset($param['end_time']) && empty($param['end_time'])){
unset($param['end_time']);
}
$link->updateUnique([
'seller_id' => $this->admin['seller_id'],
'website_id' => $param['website_id'],
'type' => $param['type'],
'name' => $param['name'],
],$param['id'],lang('友情链接名称已存在'));
$link->updateUnique([
'seller_id' => $this->admin['seller_id'],
'website_id' => $param['website_id'],
'type' => $param['type'],
'url' => $param['url'],
],$param['id'],lang('友情链接地址已存在'));
$res = $link->updateLink($where,$param);
CacheService::deleteCacheList('Link_cache_list');
return json($res);
}
return jsonReturn(-2, Lang::get('请求方法错误'));
}
/**
* 删除友情链接信息
* @return Json
* @throws ModelException
*/
public function delete(Link $link,RecycleBin $recycleBin): Json
{
if (!request()->isPost()) {
return jsonReturn(-2, Lang::get('请求方法错误'));
}
$param = input('post.');
try {
validate(LinkValidate::class)->scene('read')->check($param);
} catch (\Exception $e) {
return jsonReturn(-4, $e->getMessage());
}
$param['seller_id'] = $this->admin['seller_id'];
$where = [
'id' => $param['id'],
'seller_id' => $param['seller_id'],
'website_id' => $param['website_id'],
'is_del' => 1,
];
Db::startTrans();
try{
$res = $link->softDelLink($where);
CacheService::deleteCacheList('Link_cache_list');
if($res['data'] == 1){
$binData = [
'object_id' => $param['id'],
'table_name' => 'link',
'title' => "友情链接{$param['id']}",
'admin_id' => $this->admin['uid'],
'name' => $this->admin['name'],
];
$recycleBin->addRecycleBin($binData);
}
Db::commit();
}catch (\Exception $e){
Db::rollback();
return jsonReturn(-3,$e->getMessage());
}
return json($res);
}
/**
* @throws ModelException
*/
public function copy(Link $link): Json
{
if(!request()->isPost()){
return jsonReturn(-1, Lang::get('请求方法错误'));
}
$param = input('post.');
try {
validate(LinkValidate::class)->scene('copy')->check($param);
} catch (\Exception $e) {
return jsonReturn(-4, $e->getMessage());
}
if($param['website_id'] == $param['copy_site_id']){
return jsonReturn(-5, lang('目标网站ID和复制网站ID不能相等'));
}
$where = [
'seller_id' => $this->admin['seller_id'],
'website_id' => $param['copy_site_id'],
'is_del' => 1,
];
$selfWhere = [
'seller_id' => $this->admin['seller_id'],
'website_id' => $param['website_id'],
'is_del' => 1,
];
$field = ['name','url','sort','target','is_del'];
$linkList = $link->getAllLink($where,$field)['data']->toArray();
$selfList = $link->getAllLink($selfWhere,$field)['data']->toArray();
$flag = array_column($selfList,'name');
if(!empty($linkList)){
foreach($linkList as $key => &$val){
$val['website_id'] = $param['website_id'];
if(in_array($val['name'],$flag)){
unset($linkList[$key]);
}
}
unset($val);
$res = json($link->addAllLink($linkList));
}else{
$res = jsonReturn(0,0,Lang::get('成功'));
}
return $res;
}
}

View File

@ -0,0 +1,105 @@
<?php
namespace app\controller\backend;
use app\model\PosterMaterial;
use app\model\SysSetting;
use app\service\upload\Upload;
use think\facade\Lang;
class MaterialController extends BaseController
{
public function index()
{
$posterModel = new PosterMaterial();
$list = $posterModel->getMaterialList([]);
foreach ($list['data'] as $v) {
$v['source'] = str_replace("\\", '/', $v['source']);
}
return json($list);
}
public function upload()
{
if (request()->isPost()) {
set_time_limit(0);
$file = request()->file('file');
if (empty($file)) {
return jsonReturn(-8, Lang::get('文件上传失败,请重新尝试'));
}
$imageSize = getimagesize($file);
$seller_id = $this->admin['seller_id'];
// 查看文件类型
$fileName = $file->getOriginalName();
$fileExt = $file->getOriginalExtension();
$file_type = fileFormat($fileName);
// 附件大小和类型验证
// 获取上传配置
$Settings = new SysSetting();
$uploadSetting = $Settings->getAllCustomArrayData(['parent_id' => 1, 'group' => 'upload', 'status' => 1], 'id desc', 'id,group,title,value')['data'];
$uploadSetting = getColumnForKeyArray($uploadSetting, 'title');
$limitSize = $uploadSetting[$file_type . '_size']['value'] * 1024; // byte
$fileSize = $file->getSize(); // 单位byte
if ($fileSize > $limitSize) {
return jsonReturn(-1, Lang::get('文件过大,请修改上传限制或者替换小的文件'));
}
$extArr = explode(',', $uploadSetting[$file_type . '_ext']['value']);
if (!in_array($fileExt, $extArr)) {
return jsonReturn(-2, Lang::get('文件格式错误,请重新上传'));
}
$type = $this->getUploadType();
$upload = new Upload();
$upload->create($file, $seller_id, $type, $file_type);
$res = $upload->getUploadFileInfo()['data'];
$res['width'] = $imageSize[0];
$res['height'] = $imageSize[1];
$res['type'] = $fileExt;
if (strpos($res['url'], 'http') === false) {
$res['url'] = request()->domain() . '/' . $res['url'];
}
return jsonReturn(0, Lang::get('上传成功'), $res);
}
return jsonReturn(-3, Lang::get('请求方法错误'));
}
public function add()
{
if (request()->isPost()) {
$param = input('post.');
$posterModel = new PosterMaterial();
return json($posterModel->addMaterial($param));
}
}
public function del()
{
$id = input('param.id');
$posterModel = new PosterMaterial();
return json($posterModel->delMaterial($id));
}
/**
* @throws \app\exception\ModelException
*/
public function getUploadType()
{
// 文件信息提取
$where = [
'seller_id' => $this->admin['seller_id'],
'group' => 'upload',
'title' => 'storage'
];
$place = new SysSetting();
return $place->getSysSetting($where)['data']->toArray()['value'];
}
}

View File

@ -0,0 +1,163 @@
<?php
declare (strict_types = 1);
namespace app\controller\backend;
use app\model\Module;
use app\service\CacheService;
use app\service\ModuleFieldService;
use app\service\ModuleService;
use app\validate\ModuleValidate;
use think\exception\ValidateException;
use think\facade\Lang;
class ModuleController extends BaseController
{
/**
* 资源列表
* @param Module $module
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function index(Module $module): \think\response\Json
{
$where = [
'seller_id' => $this->admin['seller_id'],
];
$limit = $this->setLimit();
$moduleList = $module->getModuleList($where,$limit);
return json(pageReturn($moduleList));
}
/**
* 保存新建的资源
*
* @return \think\Response\Json
* @throws \Exception
*/
public function save(ModuleService $moduleService): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
// 数据验证
try{
validate(ModuleValidate::class)->scene('save')->check($param);
}catch(ValidateException $e){
return jsonReturn(-1, $e->getError());
}
// 数据库前缀
$prefix = config('database.connections')[config('database.default')]['prefix'];
$param['database_table'] = $prefix . $param['table'];
$param['status'] = 1;
$param['seller_id'] = $this->admin['seller_id'];
$res = $moduleService -> save($param);
// 系统模型添加
// $model = new Module();
// $res = $model->addModule($param);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 显示指定的资源
*
* @return \think\Response\Json
* @throws \app\exception\ModelException
* @throws \app\exception\ModelEmptyException
*/
public function read(Module $module): \think\response\Json
{
$id = (int)input('id');
if(!$id){
return jsonReturn(-1,Lang::get('模型ID不能为空'));
}
$where = [
'id' => $id,
'seller_id' => $this->admin['seller_id']
];
$res = $module->getModule($where);
return json($res);
}
/**
* 保存更新的资源
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \ReflectionException
*/
public function update(Module $module): \think\response\Json
{
if(request()->isPost()){
$param = request()->only(['id','title','description','status']);
try {
validate(ModuleValidate::class)->scene('update')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
$where = [
'id' => $param['id'],
'seller_id' => $this->admin['seller_id'],
];
$res = $module -> updateModule($where,$param);
CacheService::deleteRelationCacheByObject($module);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 删除指定资源
*
* @throws \app\exception\ModelException
*/
public function delete(ModuleService $moduleService): \think\response\Json
{
if(request()->isPost()){
$id = (int)input('id');
if(!$id){
return jsonReturn(-1,'ErrorMsg');
}
$param = [
'id' => $id,
'seller_id' => $this->admin['seller_id']
];
$res = $moduleService->destroy($param);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 模型表
* @param ModuleService $moduleService
* @return \think\response\Json
*/
public function all(ModuleService $moduleService): \think\response\Json
{
return $moduleService -> getAllModuleTable($this->admin['seller_id']);
}
/**
* 模型表字段
* @param ModuleFieldService $moduleFieldService
* @return \think\response\Json
*/
public function field(ModuleFieldService $moduleFieldService): \think\response\Json
{
$table = $this->request->param('table');
if(empty($table)){
return jsonReturn(-1,Lang::get('表名不能为空'));
}
$res = $moduleFieldService -> getModuleTableField($table);
return json($res);
}
}

View File

@ -0,0 +1,159 @@
<?php
declare (strict_types = 1);
namespace app\controller\backend;
use app\model\ModuleField;
use app\service\ContentService;
use app\service\ModuleFieldService;
use app\validate\ModuleFieldValidate;
use think\exception\ValidateException;
use think\facade\Lang;
class ModuleFieldController extends BaseController
{
protected $paramKeys = ['id','module_id','form_title','table_field','validate_rule',
'type','length','default','status','placeholder','is_null','form_validate',
'order','settings','attach_data'
];
/**
* 显示资源列表
*
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function index(ModuleField $moduleField): \think\response\Json
{
$moduleId = (int)input('module_id');
if(!$moduleId){
return jsonReturn(-1,Lang::get('模型ID不能为空'));
}
$where = [
'seller_id' => $this->admin['seller_id'],
'module_id' => $moduleId,
];
$moduleFieldList = $moduleField->where($where)->select()->each(function(&$item){
if($item['form_type'] == 'reference'){
$service = new ContentService();
$item['attach_data'] = $service->getModuleContent($item['settings']['table'],$item['seller_id'])['data'];
}
})->toArray();
$list = getColumnForKeyArray($moduleFieldList,'order');
$len = count($list);
$tmp = [];
$res = $this->sortField($list,$len,'id',$tmp);
return jsonReturn(0,Lang::get('成功'),$res);
}
/**
* 保存新建的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelEmptyException
* @throws \app\exception\ModelException
*/
public function save(ModuleFieldService $moduleFieldService,ModuleField $field): \think\response\Json
{
if(request()->isPost()){
$param = request()->only($this->paramKeys);
// 数据验证
try{
validate(ModuleFieldValidate::class)->scene('save')->check($param);
}catch(ValidateException $e){
return jsonReturn(-1, $e->getError());
}
$param['seller_id'] = $this->admin['seller_id'];
$res = $moduleFieldService->save($param);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 显示指定的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \app\exception\ModelEmptyException
*/
public function read(ModuleField $moduleField): \think\response\Json
{
$id = (int)input('id');
if(!$id){
return jsonReturn(-1,'字段ID不能为空');
}
$where = [
'id' => $id,
'seller_id' => $this->admin['seller_id']
];
// 其他逻辑
$res = $moduleField->getModuleField($where);
return json($res);
}
/**
* 保存更新的资源
* @return \think\response\Json
* @throws \app\exception\ModelEmptyException
* @throws \app\exception\ModelException
*/
public function update(ModuleFieldService $moduleFieldService): \think\response\Json
{
if(request()->isPost()){
$param = request()->only($this->paramKeys);
try {
validate(ModuleFieldValidate::class)->scene('update')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
$param['seller_id'] = $this->admin['seller_id'];
$res = $moduleFieldService -> update($param);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 删除指定资源
*
* @throws \app\exception\ModelException
* @throws \app\exception\ModelEmptyException
*/
public function delete(ModuleFieldService $moduleField): \think\response\Json
{
if(request()->isPost()){
$param = request()->only(['id','module_id']);
try {
validate(ModuleFieldValidate::class)->scene('delete')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
$where = [
'id' => $param['id'],
'module_id' => $param['module_id'],
'seller_id' => $this->admin['seller_id']
];
$res = $moduleField->destroy($where);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
public function sortField($data,$len,$key,&$tmp)
{
if(isset($data[$key])){
array_push($tmp,$data[$key]);
if($len > 0){
$this->sortField($data,$len,$data[$key]['table_field'],$tmp);
}
}
return $tmp;
}
}

View File

@ -0,0 +1,166 @@
<?php
declare (strict_types = 1);
namespace app\controller\backend;
use app\model\Nav;
use app\model\NavCate;
use app\service\CacheService;
use app\validate\NavCateValidate;
use think\exception\ValidateException;
use think\facade\Lang;
class NavCateController extends BaseController
{
/**
* 显示资源列表
*
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function index(NavCate $navCate): \think\response\Json
{
$siteId = (int)input('website_id');
if(!$siteId){
return jsonReturn(-1,Lang::get('网站ID不能为空'));
}
$lang = input('lang');
$where = [
'seller_id' => $this->admin['seller_id'],
'website_id' => $siteId,
'lang' => $lang
];
$navCateList = $navCate->getAllCustomArrayData($where,'id asc');
return json($navCateList);
}
/**
* 保存新建的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \app\exception\ModelNotUniqueException
* @throws \ReflectionException
*/
public function save(NavCate $navCate): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
// 数据验证
try{
validate(NavCateValidate::class)->scene('save')->check($param);
}catch(ValidateException $e){
return jsonReturn(-1, $e->getError());
}
// 唯一验证
$navCate -> saveUnique(
[
'seller_id'=>$this->admin['seller_id'],
'website_id' => $param['website_id'],
'title'=>$param['title'],
'lang' => $param['lang'],
],'导航分类已存在');
$res = $navCate -> addNavCate($param);
CacheService::deleteRelationCacheByObject($navCate);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 显示指定的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \app\exception\ModelEmptyException
*/
public function read(NavCate $navCate): \think\response\Json
{
$id = (int)input('id');
if(!$id){
return jsonReturn(-1,lang('导航分类ID不能为空'));
}
$where = [
'id' => $id,
'seller_id' => $this->admin['seller_id']
];
$res = $navCate->getNavCate($where);
return json($res);
}
/**
* 保存更新的资源
* @return \think\response\Json
* @throws \app\exception\ModelNotUniqueException
* @throws \app\exception\ModelException
* @throws \ReflectionException
*/
public function update(NavCate $navCate): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
try {
validate(NavCateValidate::class)->scene('update')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
// 唯一验证
$navCate -> updateUnique(
[
'seller_id'=>$this->admin['seller_id'],
'website_id' => $param['website_id'],
'title'=>$param['title'],
'lang' => $param['lang'],
],$param['id'],lang('导航分类已存在'));
$where = [
'id' => $param['id'],
'seller_id' => $this->admin['seller_id'],
];
$res = $navCate -> updateNavCate($where,$param);
CacheService::deleteRelationCacheByObject($navCate);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 删除指定资源
*
* @throws \app\exception\ModelException
* @throws \ReflectionException
*/
public function delete(NavCate $navCate,Nav $Nav): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
try {
validate(NavCateValidate::class)->scene('delete')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
$whereNav = [
'nav_cate_id' => $param['id'],
'seller_id' => $this->admin['seller_id'],
'website_id' => $param['website_id'],
];
$nav = $Nav->getNavList($whereNav)['data']->toArray();
if(!empty($nav)){
return jsonReturn(-2,lang('分类下有菜单不能直接删除'));
}
$where = [
'id' => $param['id'],
'seller_id' => $this->admin['seller_id'],
'website_id' => $param['website_id'],
];
$res = $navCate->delNavCate($where);
CacheService::deleteRelationCacheByObject($navCate);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
}

View File

@ -0,0 +1,217 @@
<?php
declare (strict_types = 1);
namespace app\controller\backend;
use app\model\Category;
use app\model\Model;
use app\model\Nav;
use app\model\NavCate;
use app\model\Route;
use app\service\CacheService;
use app\validate\NavValidate;
use think\exception\ValidateException;
use think\facade\Lang;
class NavController extends BaseController
{
/**
* 导航列表
*
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \app\exception\ModelEmptyException
*/
public function index(Nav $nav): \think\response\Json
{
$param = input('param.');
try{
validate(NavValidate::class)->scene('index')->check($param);
}catch (ValidateException $e){
return jsonReturn(-1,$e->getError());
}
$where = [
'seller_id' => $this->admin['seller_id'],
'nav_cate_id' => $param['cate_id'],
'website_id' => $param['website_id'],
'lang' => $param['lang'],
];
$navList = $nav->getAllCustomArrayData($where)['data'];
$navList = makeTree($navList);
return jsonReturn(0,'success',$navList);
}
/**
* 新增导航
*
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \app\exception\ModelEmptyException
* @throws \app\exception\ModelNotUniqueException
* @throws \ReflectionException
*/
public function save(Nav $nav,NavCate $NavCate): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
// 数据验证
try{
validate(NavValidate::class)->scene('save')->check($param);
}catch(ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
$param['seller_id'] = $this->admin['seller_id'];
if(empty($param['sort'])){
// 设置排序
$maxOrder = $nav->getMaxOrderNav(['parent_id' => $param['parent_id'],'seller_id' => $this->admin['seller_id']], 'sort')['data'];
if (!empty($maxOrder)) {
$param['sort'] = (int)$maxOrder + 10;
}
}
// 设置网站ID
$navCate = $NavCate -> getNavCate(['id'=>$param['nav_cate_id'],'seller_id'=>$this->admin['seller_id']])['data'];
$param['website_id'] = $navCate['website_id'];
// 唯一性验证
$nav->saveUnique(['title'=>$param['title'],'lang'=>$param['lang'],'nav_cate_id'=>$param['nav_cate_id'],'website_id'=>$param['website_id'],'seller_id'=>$param['seller_id'],'parent_id'=>$param['parent_id']],'导航已存在');
// 设置URL
if($param['type'] == 2){
$this->setNavUrl($param);
}
$res = $nav -> addNav($param);
CacheService::deleteRelationCacheByObject($nav);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 导航详情
*
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \app\exception\ModelEmptyException
*/
public function read(Nav $nav): \think\response\Json
{
$param = input('param.');
try {
validate(NavValidate::class)->scene('read')->check($param);
}catch (ValidateException $e){
return jsonReturn(-1,$e->getError());
}
$where = [
'id' => $param['id'],
'seller_id' => $this->admin['seller_id'],
'website_id' => $param['website_id'],
];
$res = $nav->getNav($where);
return json($res);
}
/**
* 更新导航
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \app\exception\ModelEmptyException
* @throws \app\exception\ModelNotUniqueException
* @throws \ReflectionException
*/
public function update(Nav $nav, NavCate $NavCate): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
try {
validate(NavValidate::class)->scene('update')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
// 设置网站ID
$navCate = $NavCate -> getNavCate(['id'=>$param['nav_cate_id'],'seller_id'=>$this->admin['seller_id']])['data'];
$param['website_id'] = $navCate['website_id'];
// 唯一性验证
$nav->updateUnique(['title'=>$param['title'],'website_id'=>$param['website_id'],'nav_cate_id'=>$param['nav_cate_id'],'seller_id'=>$this->admin['seller_id']],$param['id'],'导航已存在');
// 设置url
if($param['type'] == 2){
$this->setNavUrl($param);
}
if(empty($param['sort'])){
// 设置排序
$maxOrder = $nav->getMaxOrderNav(['parent_id' => $param['parent_id'],'seller_id' => $this->admin['seller_id']], 'sort')['data'];
if (!empty($maxOrder)) {
$param['sort'] = (int)$maxOrder + 10;
}
}
$where = [
'id' => $param['id'],
'seller_id' => $this->admin['seller_id'],
];
$res = $nav -> updateNav($where,$param);
CacheService::deleteRelationCacheByObject($nav);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 删除导航
*
* @throws \app\exception\ModelException
* @throws \app\exception\ModelEmptyException
* @throws \ReflectionException
*/
public function delete(Nav $nav): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
try {
validate(NavValidate::class)->scene('delete')->check($param);
}catch(ValidateException $e){
return jsonReturn(-1,$e->getError());
}
$where = [
'id' => $param['id'],
'seller_id' => $this->admin['seller_id'],
'website_id' => $param['website_id'],
];
$children = $nav->getNavList([
'parent_id' => $param['id'],
'seller_id' => $this->admin['seller_id'],
'website_id' => $param['website_id'],
])['data']->toArray();
if(count($children)){
$res = ['code'=>-4,'msg'=>lang('有子菜单不能删除')];
}else{
$res = $nav->delNav($where);
CacheService::deleteRelationCacheByObject($nav);
}
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 设置导航路由
* @throws \app\exception\ModelEmptyException
* @throws \app\exception\ModelException
*/
public function setNavUrl(&$param)
{
$Category = new Category();
$category = $Category -> getCategory(['id'=>$param['category_id'],'seller_id' => $this->admin['seller_id']])['data'];
if($category['type'] == 2){
$param['href'] = 'javascript:;';
}else if($category['type'] == 3){
$param['href'] = $category['alias'];
} else{
$Route = new Route();
$route = $Route -> getRoute(['full_url'=> 'List/index?id='.$param['category_id'],'seller_id'=>$this->admin['seller_id']])['data'];
$param['href'] = $route['url'];
}
}
}

View File

@ -0,0 +1,52 @@
<?php
namespace app\controller\backend;
use app\model\Plugin as PluginModel;
use app\service\PluginService;
use think\facade\Lang;
class PluginController extends BaseController
{
public function index()
{
$pluginModel = new PluginModel();
$plugins = $pluginModel->getList();
return jsonReturn(0, Lang::get('成功'), $plugins);
}
public function install()
{
$param = $this->request->only(['name' => '']);
$res = PluginService::install($param['name']);
if ($res === true) {
return jsonReturn(0, lang('安装成功'));
}
return jsonReturn(-1, lang('安装失败') . $res);
}
public function uninstall()
{
$param = $this->request->only(['name' => '']);
$res = PluginService::uninstall($param['name']);
if ($res === true) {
return jsonReturn(0, lang('卸载成功'));
}
return jsonReturn(-1, lang('插件卸载失败') . $res);
}
public function editStatus()
{
$param = $this->request->only(['name' => '', 'status' => 1]);
$res = PluginService::editStatus($param['name'], $param['status']);
return json($res);
}
}

View File

@ -0,0 +1,185 @@
<?php
namespace app\controller\backend;
use app\model\Poster;
use Kkokk\Poster\PosterManager;
class PosterController extends BaseController
{
public function index()
{
$posterModel = new Poster();
return json($posterModel->getPosterList([]));
}
public function add()
{
if (request()->isPost()) {
$param = input('post.');
$param['id'] = $param['id'] ?? 0;
if (empty($param['name'])) {
return jsonReturn(-1, lang('请填写海报名称'));
}
$posterModel = new Poster();
if ($posterModel->where([
['id', '<>', $param['id']],
['name', '=', $param['name']]
])->value('id')) {
return jsonReturn(-1, lang('海报名称已存在'));
}
try {
$preview = $this->makePreview($param, 'cover');
} catch (\Exception $e) {
return jsonReturn(-3, 'error', $e->getTrace());
}
$addParam = [
'name' => $param['name'],
'preview' => request()->domain() . '/' . ltrim($preview['url'], './'),
'status' => 1,
'design_content' => json_encode($param)
];
if (!empty($param['id'])) {
return json($posterModel->editPoster([['id', '=', $param['id']]],$addParam));
}
return json($posterModel->addPoster($addParam));
}
}
public function copy() {
if (!request()->isPost()) {
return jsonReturn(-1, lang('请求错误'));
}
$param = input('post.');
if (empty($param['id'])) {
return jsonReturn(-2, lang('参数错误'));
}
$posterModel = new Poster();
$data = $posterModel->where('id', '=', $param['id'])->findOrEmpty();
if (empty($data)) {
return jsonReturn(-3, lang('找不到要复制的海报'));
}
$designContent = json_decode($data['design_content'], true);
$designContent['name'] = $designContent['name'] . lang('-副本');
unset($designContent['id']);
$addParam = [
'name' => $designContent['name'],
'preview' => $data['preview'],
'status' => 1,
'design_content' => json_encode($designContent)
];
$res = $posterModel->addPoster($addParam);
if ($res['code'] == 0) {
$res['msg'] = lang('复制成功');
}
return json($res);
}
public function edit()
{
$id = input('param.id');
$posterModel = new Poster();
return json($posterModel->getDesignInfoById($id));
}
public function del()
{
$id = input('param.id');
$posterModel = new Poster();
return json($posterModel->del($id));
}
public function preview()
{
try {
$posterData = input('post.');
$result = $this->makePreview($posterData);
return jsonReturn(0, 'success', request()->domain() . '/' . ltrim($result['url'], './'));
} catch (\Exception $e) {
file_put_contents('./error.log', $e->getFile() . ' --- ' . $e->getLine() . ' --- ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
return jsonReturn(-1, $e->getMessage());
}
}
/**
* @param $posterData
* @param $type
* @return mixed
*/
protected function makePreview($posterData, $type = 'temp')
{
$name = uniqid();
if ($type == 'temp') {
$PosterManager = new PosterManager('./poster_preview/temp/' . $name . '.' . $posterData['type']);
} else {
$PosterManager = new PosterManager('./poster_preview/cover/' . $name . '.' . $posterData['type']);
}
$temp = $PosterManager;
if ($posterData['activeName'] == 'img' && !empty($posterData['img_src'])) {
$temp = $temp->buildImDst($posterData['img_src'], $posterData['width'], $posterData['height']);
} else {
$temp = $temp->buildIm($posterData['width'], $posterData['height'], $this->hex2rgba($posterData['color']));
}
if (isset($posterData['item']) && !empty($posterData['item'])) {
foreach ($posterData['item'] as $vo) {
if ($vo['t'] == 'text') {
$font = '/system_file/alifont/' . $vo['fn'] . '.woff';
$temp = $temp->buildText($vo['v'], $vo['x'], $vo['y'], $vo['s'], $this->hex2rgba($vo['c']), $vo['w'], $font, $vo['a'], $vo['w'], 1, 0, $vo['rotate']);
} else if ($vo['t'] == 'image') {
$temp = $temp->buildImage($vo['v'], $vo['x'], $vo['y'], 0, 0, $vo['w'], $vo['h'], $this->hex2rgba($vo['c']), 'normal', $vo['rotate']);
} else if ($vo['t'] == 'qrcode') {
$temp = $temp->buildQr($vo['v'], $vo['x'], $vo['y'], 0, 0, $vo['w'], $vo['h'], 4, 1, $vo['rotate']);
}
}
}
return $temp->getPoster();
}
protected function hex2rgba($color)
{
$hexColor = str_replace('#', '', $color);
$lens = strlen($hexColor);
if ($lens != 3 && $lens != 6) {
return false;
}
$newColor = '';
if ($lens == 3) {
for ($i = 0; $i < $lens; $i++) {
$newColor .= $hexColor[$i] . $hexColor[$i];
}
} else {
$newColor = $hexColor;
}
$hex = str_split($newColor, 2);
$rgba = [];
foreach ($hex as $vls) {
$rgba[] = hexdec($vls);
}
$rgba[] = 1;
return $rgba;
}
}

View File

@ -0,0 +1,121 @@
<?php
declare (strict_types = 1);
namespace app\controller\backend;
use app\model\RecycleBin;
use app\service\ModuleFieldService;
use think\facade\Db;
use think\facade\Lang;
class RecycleBinController extends BaseController
{
/**
* 显示资源列表
*
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function index(RecycleBin $recycleBin): \think\response\Json
{
$where = [
'seller_id' => $this->admin['seller_id'],
];
$table = input('param.table_name') ?: '';
if($table){
$where['table_name'] = $table;
}
$limit = $this->setLimit();
$recycleBinList = $recycleBin->getRecycleBinList($where,$limit);
return json(pageReturn($recycleBinList));
}
/**
* 回收站清空
*
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function clean(RecycleBin $recycleBin): \think\response\Json
{
if(request()->isPost()){
$res = $recycleBin -> delRecycleBin(['seller_id'=>$this->admin['seller_id']]);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 单条数据恢复
*
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \app\exception\ModelEmptyException
* @throws \think\db\exception\DbException
*/
public function restore(ModuleFieldService $fieldService): \think\response\Json
{
if(request()->isPost()){
$id = (int)input('id');
if(!$id){
return jsonReturn(-1,Lang::get('数据ID不能为空'));
}
return $fieldService->resotreData($id,$this->admin['seller_id']);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 删除指定资源
*
* @throws \app\exception\ModelException
*/
public function delete(RecycleBin $recycleBin): \think\response\Json
{
if(request()->isPost()){
$id = (int)input('id');
if(!$id){
return jsonReturn(-1,Lang::get('数据ID不能为空'));
}
$where = [
'id' => $id,
'seller_id' => $this->admin['seller_id']
];
$res = $recycleBin->delRecycleBin($where);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 恢复多条数据
* @throws \think\db\exception\DbException
*/
public function allRestore(RecycleBin $recycleBin): \think\response\Json
{
if(!request()->isPost()){
return jsonReturn(-4,Lang::get('请求方法错误'));
}
$ids = input('param.ids');
if(!is_array($ids)){
return jsonReturn(-1,Lang::get('恢复数据参数错误'));
}
if(empty($ids)){
return jsonReturn(-2,Lang::get('恢复数据参数不能为空'));
}
$tableName = $recycleBin->whereIn('id',$ids)->column('table_name');
if(empty($tableName)){
return jsonReturn(-5,Lang::get('恢复数据不存在'));
}
$table = array_unique($tableName);
if(count($table) > 1){
return jsonReturn(-3,Lang::get('请选择相同类型的内容恢复'));
}
$recycleBin->whereIn('id',$ids)->where('seller_id',$this->admin['seller_id'])->delete();
$res = Db::name($table[0])->whereIn('id',$ids)->update(['is_del'=>1,'delete_time'=>0,'update_time'=>time()]);
return jsonReturn(0,Lang::get('恢复成功'),$res);
}
}

View File

@ -0,0 +1,182 @@
<?php
declare (strict_types=1);
namespace app\controller\backend;
use app\model\Admin;
use app\model\AdminMenu;
use app\model\Role;
use app\service\RoleService;
use app\validate\RoleValidate;
use think\exception\ValidateException;
use think\facade\Cache;
use think\facade\Lang;
class RoleController extends BaseController
{
/**
* 显示资源列表
*
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function index(Role $role): \think\response\Json
{
$where = [
'seller_id' => $this->admin['seller_id'],
];
$roleList = $role->getRoleList($where);
return json($roleList);
}
/**
* 保存新建的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \app\exception\ModelNotUniqueException
*/
public function save(Role $role): \think\response\Json
{
if (request()->isPost()) {
$param = request()->only(['title', 'status']);
// 数据验证
try {
validate(RoleValidate::class)->scene('save')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
$param['seller_id'] = $this->admin['seller_id'];
$role->saveUnique(['seller_id' => $param['seller_id'], 'title' => $param['title']], lang('角色已经存在'));
$res = $role->addRole($param);
return json($res);
}
return jsonReturn(-3, Lang::get('请求方法错误'));
}
/**
* 显示指定的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \app\exception\ModelEmptyException
*/
public function read(Role $role): \think\response\Json
{
$id = (int)input('id');
if (!$id) {
return jsonReturn(-1, Lang::get('角色ID不能为空'));
}
$where = [
'id' => $id,
'seller_id' => $this->admin['seller_id']
];
$res = $role->getRole($where, ['website']);
if (!empty($res['data']['kid_auth'])) {
$res['data']['auth'] = explode(',', $res['data']['kid_auth']);
} else {
$res['data']['auth'] = [];
}
$res['data']['website_id'] = array_column($res['data']['website']->toArray(), 'id');
return json($res);
}
/**
* 保存更新的资源
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \app\exception\ModelNotUniqueException
* @throws \app\exception\ModelEmptyException
*/
public function update(Role $role): \think\response\Json
{
if (request()->isPost()) {
$param = input('post.');
try {
validate(RoleValidate::class)->scene('update')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
if ($param['group'] == 1) {
jsonReturn(-2, Lang::get('系统默认角色,不能编辑'));
}
$where = [
'id' => $param['id'],
'seller_id' => $this->admin['seller_id'],
];
$roleInfo = $role->getRole($where)['data'];
if (empty($roleInfo)) {
return jsonReturn(-3, Lang::get('角色不存在'));
}
$role->updateUnique(['seller_id' => $this->admin['seller_id'], 'title' => $param['title']], $param['id'], lang('角色已经存在'));
if (!empty($param['auth'])) {
$len = count($param['auth']);
foreach ($param['auth'] as $key => $val) {
if ($val == 2) {
break;
}
if ($len == $key + 1) {
$param['auth'][] = 2;
}
}
$param['auth'] = implode(',', $param['auth']);
}
$roleInfo->website()->detach();
$roleInfo->website()->attach($param['website_id']);
unset($param['website_id']);
$param['kid_auth'] = implode(',', $param['kid_auth']);
$res = $role->updateRole($where, $param);
return json($res);
}
return jsonReturn(-3, Lang::get('请求方法错误'));
}
/**
* 删除指定资源
*
* @throws \app\exception\ModelException
*/
public function delete(Role $role): \think\response\Json
{
if (request()->isPost()) {
$id = (int)input('id');
if (!$id) {
return jsonReturn(-1, Lang::get('Id不能为空'));
}
if ($id == 1) {
return jsonReturn(-2, Lang::get('系统默认角色,不能删除'));
}
$where = [
'id' => $id,
'seller_id' => $this->admin['seller_id']
];
$res = $role->delRole($where);
return json($res);
}
return jsonReturn(-3, Lang::get('请求方法错误'));
}
/**
* @throws \app\exception\ModelException
*/
public function getAuth(AdminMenu $AdminMenu): \think\response\Json
{
$auth = $AdminMenu->getAllAdminMenu(['seller_id' => $this->admin['seller_id'], 'status' => 1])['data']->toArray();
$auth = generate($auth);
return jsonReturn(0, Lang::get('成功'), $auth);
}
public function getMenuAndUpdateAuth()
{
$adminId = $this->admin['uid'];
$res = RoleService::getMenuAndUpdateAuth($adminId);
return json($res);
}
}

View File

@ -0,0 +1,80 @@
<?php
namespace app\controller\backend;
use app\model\SeoAccount;
use app\validate\SeoAccountValidate;
use think\exception\ValidateException;
use think\response\Json;
/**
* SEO账号灌管理
*/
class SeoAccountController extends BaseController
{
public function lists(SeoAccount $seoModel): Json
{
$limit = input('param.limit');
$where = [];
$list = $seoModel->getAccountList($where, $limit);
return json(pageReturn($list));
}
public function add(SeoAccount $seoModel): Json
{
$param = input('post.');
$has = $seoModel->field('id')->where('account', $param['account'])->find();
if (!empty($has)) {
return jsonReturn(-2, lang('该账号已经存在'));
}
try {
validate(SeoAccountValidate::class)->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
$param['create_time'] = date('Y-m-d H:i:s');
$seoModel->insert($param);
return jsonReturn(0, lang('添加账号成功'));
}
public function info(SeoAccount $seoModel): Json
{
$info = $seoModel->where('id', input('param.id'))->find();
return jsonReturn(0, lang('查询成功'), $info);
}
public function edit(SeoAccount $seoModel): Json
{
$param = input('post.');
$has = $seoModel->field('id')->where('account', $param['account'])->where('id', '<>', $param['id'])->find();
if (!empty($has)) {
return jsonReturn(-2, lang('该账号已经存在'));
}
try {
validate(SeoAccountValidate::class)->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
$param['update_time'] = date('Y-m-d H:i:s');
$seoModel->where('id', $param['id'])->update($param);
return jsonReturn(0, lang('编辑账号成功'));
}
public function del(SeoAccount $seoModel)
{
$seoModel->where('id', input('param.id'))->delete();
return jsonReturn(0, lang('删除成功'));
}
}

View File

@ -0,0 +1,242 @@
<?php
declare (strict_types=1);
namespace app\controller\backend;
use think\facade\Db;
class SeoCheckController extends BaseController
{
public function index()
{
$limit = input('param.limit');
$where = [];
$list = Db::name('seo_check_task')->where($where)->order('create_time desc')->paginate($limit);
return json(pageReturn(['code' => 0, 'data' => $list, 'msg' => lang('成功')]));
}
public function check()
{
$param = $this->request->param();
if ($param['flag'] == 'progress') {
$taskDetail = Db::name('seo_check_task_detail')->where('task_id', $param['task_id'])->select();
$isEnd = true;
$checkData = [];
$success = 0;
$error = 0;
foreach ($taskDetail as $vo) {
if ($vo['status'] == 1) {
$isEnd = false;
}
if (!empty($vo['remark'])) {
$data = json_decode($vo['remark'], true);
$checkData[] = $data;
if ($data['code'] == 101) {
foreach ($data['data'] as $key => $v) {
if ($v['status'] == 1) {
$success++;
continue;
}
if ($key == 'tags') {
$error += isset($v['msg']['ajax']) ? count($v['msg']['ajax']) : 0;
$error += isset($v['msg']['alt']) ? count($v['msg']['alt']) : 0;
continue;
}
if (is_array($v['msg'])) {
$error += count($v['msg']);
} else {
$error ++;
}
}
}
if ($data['code'] == 102) {
if ($data['data']['status'] == 1) {
$success++;
} else {
$error++;
}
}
if ($data['code'] == 103) {
$error += isset($data['data']['deadLink']) ? count($data['data']['deadLink']) : 0;
$error += isset($data['data']['keywords']) ? count($data['data']['keywords']) : 0;
}
if ($data['code'] == 104) {
if (isset($data['data']['inlinks']['linksHave'])) {
foreach ($data['data']['inlinks']['linksHave'] as $v) {
if ($v['status'] == 1) {
$success++;
} else {
$error++;
}
}
}
if (isset($data['data']['inlinks']['nofollow'])) {
foreach ($data['data']['inlinks']['nofollow'] as $v) {
if ($v['status'] == 1) {
$success++;
} else {
$error++;
}
}
}
if (isset($data['data']['outlinks']['linksHave'])) {
foreach ($data['data']['outlinks']['linksHave'] as $v) {
if ($v['status'] == 1) {
$success++;
} else {
$error++;
}
}
}
}
}
}
if ($isEnd) {
Db::name('seo_check_task')->where('id', $param['task_id'])->update([
'status' => 2,
'update_time' => date('Y-m-d H:i:s')
]);
}
return json(['code' => 0, 'data' => [
'success' => $success,
'error' => $error,
'check_data' => $checkData,
'is_end' => $isEnd,
'task' => Db::name('seo_check_task')->where('id', $param['task_id'])->find()
], 'msg' => 'success']);
}
$websiteId = input('param.website_id');
$list = Db::name('theme')->field('lang,theme')->where('is_active', 1)->where('website_id', $websiteId)->select();
return jsonReturn(0, 'success', $list);
}
public function start()
{
if (request()->isPost()) {
$param = input('post.');
$hasRun = Db::name('seo_check_task')->field('id')->where('website_id', $param['website_id'])->where('status', 1)->find();
if (!empty($hasRun)) {
return jsonReturn(-1, lang('该站点正在检测中'));
}
Db::startTrans();
try {
$taskId = Db::name('seo_check_task')->insertGetId([
'website_id' => $param['website_id'],
'website_url' => $param['website_url'],
'lang' => $param['lang'],
'theme' => $param['theme'],
'status' => 1,
'create_time' => date('Y-m-d H:i:s')
]);
$params = [
[
'task_id' => $taskId,
'status' => 1,
'code' => 'code',
'create_time' => date('Y-m-d H:i:s')
],
[
'task_id' => $taskId,
'status' => 1,
'code' => 'web',
'create_time' => date('Y-m-d H:i:s')
],
[
'task_id' => $taskId,
'status' => 1,
'code' => 'keywords',
'create_time' => date('Y-m-d H:i:s')
],
[
'task_id' => $taskId,
'status' => 1,
'code' => 'links',
'create_time' => date('Y-m-d H:i:s')
],
[
'task_id' => $taskId,
'status' => 1,
'code' => 'article',
'create_time' => date('Y-m-d H:i:s')
],
];
Db::name('seo_check_task_detail')->insertAll($params);
Db::commit();
} catch (\Exception $e) {
Db::rollback();
return jsonReturn(-2, $e->getMessage());
}
$taskData = [
'task_id' => $taskId,
'websiteId' => $param['website_id'],
'start_time' => date('Y-m-d H:i:s'),
'lang' => $param['lang'],
'theme' => $param['theme'],
'websiteUrl' => $param['website_url'],
'cate' => Db::name('category')->field('id,title,alias')->where('status', 1)
->where('lang', $param['lang'])
->where('type', '<>', 3)->where('website_id', $param['website_id'])->select(),
'keywords' => Db::name('keyword')->field('name')->where('is_del', 1)->where('website_id', $param['website_id'])->select()
];
// 以text协议发送$task_data数据
$taskData['cmd'] = 'code';
$this->sendData($taskData);
$taskData['cmd'] = 'web';
$this->sendData($taskData);
$taskData['cmd'] = 'keywords';
$this->sendData($taskData);
$taskData['cmd'] = 'links';
$this->sendData($taskData);
$taskData['cmd'] = 'article';
$this->sendData($taskData);
return jsonReturn(0, lang('启动成功'), $taskData);
}
}
public function del()
{
if (!request()->isPost()) {
return jsonReturn(-1, lang('请求错误'));
}
$param = $this->request->only(['id']);
Db::name('seo_check_task')->where('id', '=', $param['id'])->delete();
Db::name('seo_check_task_detail')->where('task_id', '=', $param['id'])->delete();
return jsonReturn(0, lang('删除成功'));
}
protected function sendData($data)
{
// 与服务端建立连接
$client = stream_socket_client('tcp://127.0.0.1:19890');
fwrite($client, json_encode($data) . "\n");
//关闭句柄
fclose($client);
}
}

View File

@ -0,0 +1,54 @@
<?php
namespace app\controller\backend;
use app\model\BaiduTjGather;
use app\model\SeoAccount;
class SeoController extends BaseController
{
public function baidu(SeoAccount $seoAccount, BaiduTjGather $baiduTjGather): \think\response\Json
{
$account = $seoAccount->field('id,account')->where('type', 1)->where('status', 1)->select();
$pvData = $baiduTjGather->getYesterdayData()['data'];
if (empty($pvData)) {
$pvData = [
'pv_count' => 0,
'uv_count' => 0,
'ip_count' => 0,
'avg_visit_time' => '00:00:00'
];
} else {
$pvData['avg_visit_time'] = $this->changeTimeType($pvData['avg_visit_time']);
}
$data = [
'account' => $account,
'pvData' => $pvData
];
return jsonReturn(0, lang('查询成功'), $data);
}
/**
* 计算时长
* @param $seconds
* @return string
*/
protected function changeTimeType($seconds): string
{
if ($seconds > 3600) {
$hours = intval($seconds / 3600);
$minutes = $seconds % 3600;
$time = $hours . ":" . gmstrftime('%M:%S', $minutes);
} else {
$time = gmstrftime('%H:%M:%S', $seconds);
}
return $time;
}
}

View File

@ -0,0 +1,139 @@
<?php
declare (strict_types = 1);
namespace app\controller\backend;
use app\model\SeoSetting;
use app\validate\SeoSettingValidate;
use think\exception\ValidateException;
use think\facade\Lang;
class SeoSettingController extends BaseController
{
/**
* 显示资源列表
*
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function index(SeoSetting $seoSetting): \think\response\Json
{
$where = [
'seller_id' => $this->admin['seller_id'],
];
$limit = 10;
$limitParam = (int)input('limit');
if($limitParam){
$limit = $limitParam;
}
// TODO
// 添加其他逻辑
$seoSettingList = $seoSetting->getSeoSettingList($where,$limit);
return json(pageReturn($seoSettingList));
}
/**
* 保存新建的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function save(SeoSetting $seoSetting): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
$param['seller_id'] = $this->admin['seller_id'];
// 数据验证
try{
validate(SeoSettingValidate::class)->scene('save')->check($param);
}catch(ValidateException $e){
return jsonReturn(-1, $e->getError());
}
// TODO
// 其他逻辑
$res = $seoSetting -> addSeoSetting($param);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 显示指定的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \app\exception\ModelEmptyException
*/
public function read(SeoSetting $seoSetting): \think\response\Json
{
$id = (int)input('id');
if(!$id){
// TODO
// 修改错误消息
return jsonReturn(-1,'ErrorMsg');
}
$where = [
'id' => $id,
'seller_id' => $this->admin['seller_id']
];
// TODO
// 其他逻辑
$res = $seoSetting->getSeoSetting($where);
return json($res);
}
/**
* 保存更新的资源
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function update(SeoSetting $seoSetting): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
try {
validate(SeoSettingValidate::class)->scene('update')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
$where = [
'id' => $param['id'],
'seller_id' => $this->admin['seller_id'],
];
$res = $seoSetting -> updateSeoSetting($where,$param);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 删除指定资源
*
* @throws \app\exception\ModelException
*/
public function delete(SeoSetting $seoSetting): \think\response\Json
{
if(request()->isPost()){
$id = (int)input('id');
if(!$id){
// TO DO
// 替换错误提示
return jsonReturn(-1,'ErrorMsg');
}
$where = [
'id' => $id,
'seller_id' => $this->admin['seller_id']
];
$res = $seoSetting->delSeoSetting($where);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
}

View File

@ -0,0 +1,46 @@
<?php
declare (strict_types = 1);
namespace app\controller\backend;
use app\model\Website;
use app\service\Sitemap;
use think\facade\Lang;
class SiteMapController extends BaseController
{
/**
* @throws \app\exception\ModelException
*/
public function gen() {
$website_id = input('website_id');
$where = [
'seller_id' => $this->admin['seller_id'],
'website_id' => $website_id,
];
$sitemap = new Sitemap($where['seller_id'], $where['website_id']);
$sitemap->scan();
$silian = $sitemap->siliamGen();
// 生成xml文件
$path = $sitemap->sitemapGen();
$websiteWhere = [
'id' => $website_id
];
$param = [
'sitemap_url' => $path,
'silian_url' => $silian,
];
$website = new Website();
$res = $website->updateWebsite($websiteWhere, $param);
return jsonReturn($res['code'], Lang('生成成功'), $res['data']);
}
}

View File

@ -0,0 +1,125 @@
<?php
declare (strict_types = 1);
namespace app\controller\backend;
use app\model\SlideCate;
use app\validate\SlideCateValidate;
use think\exception\ValidateException;
use think\facade\Lang;
class SlideCateController extends BaseController
{
/**
* 显示资源列表
*
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function index(SlideCate $slideCate): \think\response\Json
{
$where = [
'seller_id' => $this->admin['seller_id'],
];
$limit = $this->setLimit();
$slideCateList = $slideCate->getSlideCateList($where,$limit);
return json(pageReturn($slideCateList));
}
/**
* 保存新建的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function save(SlideCate $slideCate): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
// 数据验证
try{
validate(SlideCateValidate::class)->scene('save')->check($param);
}catch(ValidateException $e){
return jsonReturn(-1, $e->getError());
}
$res = $slideCate -> addSlideCate($param);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 显示指定的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \app\exception\ModelEmptyException
*/
public function read(SlideCate $slideCate): \think\response\Json
{
$id = (int)input('id');
if(!$id){
return jsonReturn(-1,Lang::get('分类ID不能为空'));
}
$where = [
'id' => $id,
'seller_id' => $this->admin['seller_id']
];
// TODO
// 其他逻辑
$res = $slideCate->getSlideCate($where);
return json($res);
}
/**
* 保存更新的资源
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function update(SlideCate $slideCate): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
try {
validate(SlideCateValidate::class)->scene('update')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
$where = [
'id' => $param['id'],
'seller_id' => $this->admin['seller_id'],
];
$res = $slideCate -> updateSlideCate($where,$param);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 删除指定资源
*
* @throws \app\exception\ModelException
*/
public function delete(SlideCate $slideCate): \think\response\Json
{
if(request()->isPost()){
$id = (int)input('id');
if(!$id){
return jsonReturn(-1,Lang::get('分类ID不能为空'));
}
$where = [
'id' => $id,
'seller_id' => $this->admin['seller_id']
];
$res = $slideCate->delSlideCate($where);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
}

View File

@ -0,0 +1,134 @@
<?php
declare (strict_types = 1);
namespace app\controller\backend;
use app\model\Slide;
use app\service\CacheService;
use app\validate\SlideValidate;
use think\exception\ValidateException;
use think\facade\Lang;
class SlideController extends BaseController
{
/**
* 显示资源列表
*
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function index(Slide $slide): \think\response\Json
{
$where = [
'seller_id' => $this->admin['seller_id'],
'slide_cate_id' => (int)input('slide_cate_id'),
];
$slideList = $slide->getAllCustomArrayData($where,'sort desc','*',['attachment'=>function($q){
$q->field('id,name,url,type');
}]);
return json($slideList);
}
/**
* 保存新建的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \ReflectionException
*/
public function save(Slide $slide): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
// 数据验证
try{
validate(SlideValidate::class)->scene('save')->check($param);
}catch(ValidateException $e){
return jsonReturn(-1, $e->getError());
}
$res = $slide -> addSlide($param);
CacheService::deleteRelationCacheByObject($slide);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 显示指定的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \app\exception\ModelEmptyException
*/
public function read(Slide $slide): \think\response\Json
{
$id = (int)input('id');
if(!$id){
return jsonReturn(-1,Lang::get('幻灯片ID'));
}
$where = [
'id' => $id,
'seller_id' => $this->admin['seller_id']
];
// 其他逻辑
$res = $slide->getSlide($where,['attachment'=>function($q){
$q->field('id,name,url,type');
}]);
return json($res);
}
/**
* 保存更新的资源
* @return \think\response\Json
* @throws \app\exception\ModelException
*@throws \ReflectionException
*/
public function update(Slide $slide): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
try {
validate(SlideValidate::class)->scene('update')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
$where = [
'id' => $param['id'],
'seller_id' => $this->admin['seller_id'],
];
$res = $slide -> updateSlide($where,$param);
CacheService::deleteRelationCacheByObject($slide);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 删除指定资源
*
* @throws \app\exception\ModelException
* @throws \ReflectionException
*/
public function delete(Slide $slide): \think\response\Json
{
if(request()->isPost()){
$id = (int)input('id');
if(!$id){
return jsonReturn(-1,Lang::get('幻灯片ID'));
}
$where = [
'id' => $id,
'seller_id' => $this->admin['seller_id']
];
$res = $slide->delSlide($where);
CacheService::deleteRelationCacheByObject($slide);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
}

View File

@ -0,0 +1,139 @@
<?php
declare (strict_types = 1);
namespace app\controller\backend;
use app\model\SocialMarketing;
use app\validate\SocialMarketingValidate;
use think\exception\ValidateException;
use think\facade\Lang;
class SocialMarketingController extends BaseController
{
/**
* 显示资源列表
*
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function index(SocialMarketing $socialMarketing): \think\response\Json
{
$where = [
'seller_id' => $this->admin['seller_id'],
];
$limit = 10;
$limitParam = (int)input('limit');
if($limitParam){
$limit = $limitParam;
}
// TODO
// 添加其他逻辑
$socialMarketingList = $socialMarketing->getSocialMarketingList($where,$limit);
return json(pageReturn($socialMarketingList));
}
/**
* 保存新建的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function save(SocialMarketing $socialMarketing): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
$param['seller_id'] = $this->admin['seller_id'];
// 数据验证
try{
validate(SocialMarketingValidate::class)->scene('save')->check($param);
}catch(ValidateException $e){
return jsonReturn(-1, $e->getError());
}
// TODO
// 其他逻辑
$res = $socialMarketing -> addSocialMarketing($param);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 显示指定的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \app\exception\ModelEmptyException
*/
public function read(SocialMarketing $socialMarketing): \think\response\Json
{
$id = (int)input('id');
if(!$id){
// TODO
// 修改错误消息
return jsonReturn(-1,'ErrorMsg');
}
$where = [
'id' => $id,
'seller_id' => $this->admin['seller_id']
];
// TODO
// 其他逻辑
$res = $socialMarketing->getSocialMarketing($where);
return json($res);
}
/**
* 保存更新的资源
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function update(SocialMarketing $socialMarketing): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
try {
validate(SocialMarketingValidate::class)->scene('update')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
$where = [
'id' => $param['id'],
'seller_id' => $this->admin['seller_id'],
];
$res = $socialMarketing -> updateSocialMarketing($where,$param);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 删除指定资源
*
* @throws \app\exception\ModelException
*/
public function delete(SocialMarketing $socialMarketing): \think\response\Json
{
if(request()->isPost()){
$id = (int)input('id');
if(!$id){
// TO DO
// 替换错误提示
return jsonReturn(-1,'ErrorMsg');
}
$where = [
'id' => $id,
'seller_id' => $this->admin['seller_id']
];
$res = $socialMarketing->delSocialMarketing($where);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
}

View File

@ -0,0 +1,45 @@
<?php
namespace app\controller\backend;
use app\service\StaticFileService;
class StaticFileController extends BaseController
{
public function updateCategoryCache()
{
$param = $this->request->only(['site_id', 'lang']);
$param['admin'] = $this->admin;
$staticService = new StaticFileService();
$res = $staticService->updateCategoryCache($param);
return json($res);
}
public function createCategory()
{
$param = $this->request->only(['site_id', 'lang', 'category_id' => 0, 'has_child']);
$param['admin'] = $this->admin;
$param['category_id'] = $param['category_id'] ?? 0;
$staticService = new StaticFileService();
$res = $staticService->createCategory($param['category_id'], $param, $param['has_child']);
return json($res);
}
public function createContent()
{
$param = $this->request->only(['site_id', 'lang', 'category_id' => 0]);
$param['admin'] = $this->admin;
$param['category_id'] = $param['category_id'] ?? 0;
$staticService = new StaticFileService();
$res = $staticService->createContent($param['category_id'], $param);
return json($res);
}
}

View File

@ -0,0 +1,617 @@
<?php
namespace app\controller\backend;
use app\exception\BadSysSettingException;
use app\model\BaiduTjGather;
use app\model\SeoAccount;
use app\model\SysSetting;
use app\model\Visit;
use app\model\VisitLog;
use app\model\Website;
use think\facade\Lang;
class StatisticsController extends BaseController
{
public $header = '';
public $url = 'http://api.baidu.com/json/tongji/v1/ReportService/getData';
protected $baiduInfo;
private $site_id = 0;
/**
* @throws \app\exception\ModelException
*/
public function setConfig()
{
$id = input('param.id') ?? 0;
if (!empty($id)) {
$info = (new SeoAccount())->where('id', $id)->find();
$this->baiduInfo = [
'account_type' => '1',
'username' => $info['account'],
'password' => $info['password'],
'token' => $info['token'],
];
} else {
$this->baiduInfo = null;
}
}
/**
* @throws \app\exception\ModelException
* @throws BadSysSettingException
*/
public function initialize()
{
parent::initialize();
}
/**
* @throws BadSysSettingException
*/
public function baiduErrorReturn($response)
{
if(!empty($response['header']['failures'])){
throw new BadSysSettingException($response['header']['failures'][0]['message']);
}
}
/**
* @throws BadSysSettingException
*/
public function getResponse($data)
{
$data = json_encode(['body'=>$data, "header"=>$this->header]);
$response = $this->https_request($this->url, $data);
$response = json_decode($response,true);
$this->baiduErrorReturn($response);
return $response;
}
/**
* @throws BadSysSettingException
*/
public function lists(): \think\response\Json
{
$url = 'https://api.baidu.com/json/tongji/v1/ReportService/getSiteList';
$data = json_encode(["header"=>$this->header]);
$response = $this->https_request($url, $data);
$response = json_decode($response, true);
$this->baiduErrorReturn($response);
return jsonReturn(0, Lang::get('查询成功'), $response['body']['data']['0']['list']);
}
public function indexLists()
{
$websiteModel = new Website();
$list = $websiteModel->field('*, id as site_id, domain')->select();
return jsonReturn(0, Lang::get('查询成功'), $list);
}
// 首页趋势图
public function indexQST()
{
ini_set('memory_limit', '512M');
$websiteId = $this->request->request('website_id')?$this->request->request('website_id'):1;
$type = $this->request->request('type')?$this->request->request('type'):1;
$nowDay = date('Y-m-d');
// 今天
$startTime = strtotime($nowDay);
$endTime = strtotime(date('Y-m-d', strtotime('+1 day')));
if ($type==2) { // 昨天
$startTime = strtotime(date('Y-m-d', strtotime('-1 day')));
$endTime = strtotime($nowDay) - 1;
} elseif ($type==3) { // 最近7天
$startTime = strtotime(date('Y-m-d', strtotime('-6 day')));
$endTime = strtotime(date('Y-m-d', strtotime('+1 day')));
} elseif ($type==4) { // 最近30天
$startTime = strtotime(date('Y-m-d', strtotime('-29 day')));
$endTime = strtotime(date('Y-m-d', strtotime('+1 day')));
}
$visitModel = new VisitLog();
$list = $visitModel->where([
['create_time', '>=', $startTime],
['create_time', '<=', $endTime],
['website_id', '=', $websiteId],
])->select()->toArray();
$newList = [];
if ($type == 1 || $type == 2) {
$maxH = 23;
if ($type == 1) {
$maxH = date('H');
}
for ($i = 0; $i <= $maxH; $i++) {
if ($i < 10) {
$i = '0' . $i;
}
$i = (string)$i;
$newList[$i] = 0;
}
} else {
for ($i = $startTime; $i <= $endTime;) {
$dateStr = date('Y-m-d', $i);
$newList[$dateStr] = 0;
$i += 86400;
}
}
$type1List = $type2List = $type3List = $newList;
$type2Unique = []; // 校验唯一
$type3Unique = []; // 校验唯一
// type 为1和2时间间隔为小时。否则为天
foreach ($list as $value) {
if ($type == 1 || $type == 2) {
$key = date('H', strtotime($value['create_time']));
} else {
$key = date('Y-m-d', strtotime($value['create_time']));
}
$type1List[$key] ++;
if (!isset($type2Unique[$key][$value['agent']])) {
$type2Unique[$key][$value['agent']] = 1;
$type2List[$key] ++;
}
if (!isset($type3Unique[$key][$value['ip']])) {
$type3Unique[$key][$value['ip']] = 1;
$type3List[$key] ++;
}
}
$returnData['pv_list'] = array_values($type1List);
$returnData['uv_list'] = array_values($type2List);
$returnData['ip_list'] = array_values($type3List);
$returnData['time'] = array_keys($type1List);
foreach ($returnData['time'] as &$value) {
$value = (string)$value;
}
return jsonReturn(0, Lang::get('查询成功'), $returnData);
}
// 首页地区分布图
public function indexArea()
{
$websiteId = $this->request->request('website_id')?$this->request->request('website_id'):1;
$type = $this->request->request('type')?$this->request->request('type'):1;
$nowDay = date('Y-m-d');
// 今天
$startTime = strtotime($nowDay);
$endTime = strtotime(date('Y-m-d', strtotime('+1 day')));
if ($type==2) { // 昨天
$startTime = strtotime(date('Y-m-d', strtotime('-1 day')));
$endTime = strtotime($nowDay) - 1;
} elseif ($type==3) { // 最近7天
$startTime = strtotime(date('Y-m-d', strtotime('-6 day')));
$endTime = strtotime(date('Y-m-d', strtotime('+1 day')));
} elseif ($type==4) { // 最近30天
$startTime = strtotime(date('Y-m-d', strtotime('-29 day')));
$endTime = strtotime(date('Y-m-d', strtotime('+1 day')));
}
$visitModel = new VisitLog();
$list = $visitModel->where([
['create_time', '>=', $startTime],
['create_time', '<=', $endTime],
['website_id', '=', $websiteId],
['visited_area', '<>', '内网ip'],
])->select()->toArray();
$total = count($list);
$newData = [];
foreach ($list as $value) {
$areaArr = explode('-', $value['visited_area']);
$province = $areaArr[0];
if (!isset($newData[$province])) {
$newData[$province] = [
'name' => $province,
'value' => 1,
'pv_ratio' => bcmul(1 / $total, 100, 2),
];
} else {
$newData[$province]['value']++;
$newData[$province]['pv_ratio'] = bcmul($newData[$province]['value']/$total, 100, 2);
}
}
return jsonReturn(0, Lang::get('查询成功'), array_values($newData));
}
/**
* 访客年龄分布
* @return false|string|\think\response\Json
* @throws BadSysSettingException
*/
public function age()
{
$data = [
"site_id"=> $this->request->param('site_id') ? $this->request->param('site_id') : $this->site_id,
"method" => "overview/getAge"
];
$response = $this->getResponse($data);
$response = $response['body']['data']['0']['result'];
return jsonReturn(0, '查询成功', $response);
}
/**
* 今日昨日浏览量分析
* @throws BadSysSettingException
*/
public function outline(): \think\response\Json
{
$data = [
"site_id"=> $this->request->param('site_id')?$this->request->param('site_id'):$this->site_id,
"method" => "overview/getOutline"
];
$response = $this->getResponse($data);
$response = $response['body']['data']['0']['result'];
foreach ($response['items'] as $key => $value) {
if(is_numeric($value[5]) && $value[5] != '--'){
$response['items'][$key][5] = $this->secToTime($value[5]);
}elseif(is_array($value[5]) && $value[5]['val'] != '--'){
$response['items'][$key][5]['val'] = $this->secToTime($value[5]['val']);
}
}
return jsonReturn(0, Lang::get('查询成功'),$response);
}
/**
* 趋势图
* pv_count (浏览量PV)
* visitor_count (访客数UV)
* ip_count (IP )
* bounce_ratio (跳出率,%)
* avg_visit_time (平均访问时长,秒)
* trans_count (转化次数)
* @throws BadSysSettingException
*/
public function qxt()
{
$type = $this->request->request('type')?$this->request->request('type'):1;
$contrast = $this->request->request('contrast')?$this->request->request('contrast'):1;
$metrics = $this->request->request('metrics')?$this->request->request('metrics'):'pv_count';
switch ($metrics) {
case 'visitor_count':
$name = Lang::get('访客数(UV)');
break;
case 'ip_count':
$name = Lang::get('IP数');
break;
case 'bounce_ratio':
$name = Lang::get('跳出率');
break;
case 'avg_visit_time':
$name = Lang::get('平均访问时长');
break;
case 'trans_count':
$name = Lang::get('转化次数');
break;
default:
$name = Lang::get('浏览量(PV)');
break;
}
$et = date('Ymd', time());
if($type==1){
$st = date('Ymd', time());
if($contrast==1){
$start_date2 = date('Ymd', time()-(3600*24));
$end_date2 = date('Ymd', time()-(3600*24));
}elseif($contrast==2){
$start_date2 = date('Ymd', time()-(3600*24*7));
$end_date2 = date('Ymd', time()-(3600*24*7));
}
}elseif ($type==2) {
$st = date('Ymd', strtotime('-1 day'));
$et = date('Ymd', strtotime('-1 day'));
if($contrast==1){
$start_date2 = date('Ymd', strtotime('-1 day')-(3600*24));
$end_date2 = date('Ymd', strtotime('-1 day')-(3600*24));
}elseif($contrast==2){
$start_date2 = date('Ymd', time()-(3600*24*7));
$end_date2 = date('Ymd', time()-(3600*24*7));
}
}elseif ($type==3) {
$st = date('Ymd', strtotime('-6 day'));
}elseif ($type==4) {
$st = date('Ymd', strtotime('-29 day'));
}
if($contrast && ($type == 1 or $type == 2)){
$data = [
"site_id"=> $this->request->param('site_id')?$this->request->param('site_id'):$this->site_id,
"start_date" => $st,
"end_date" => $et,
"start_date2" => $start_date2,
"end_date2" => $end_date2,
"metrics" => $metrics,
"method" => "overview/getTimeTrendRpt"
];
}else{
$data = [
"site_id"=> $this->request->param('site_id')?$this->request->param('site_id'):$this->site_id,
"start_date" => $st,
"end_date" => $et,
"metrics" => $metrics,
"method" => "overview/getTimeTrendRpt"
];
}
$response = $this->getResponse($data);
$result = $response['body']['data']['0']['result']['items'];
$new = [];
$new[0] = $result[1];
$new[1] = $result[2];
if($metrics != 'avg_visit_time'){
foreach ($new as $key => $value) {
foreach ($value as $k => $v) {
$count = count($value);
if($type == 3 || $type == 4){
$new[$key][$k][0] = date('Y/m/d', strtotime('-'.($count-$k-1).' day'));
$new[$key][$k][1] = $v[0];
}
if($k<=10){
array_push($new[$key][$k], $k.':00-'.$k.':59');
}else{
array_push($new[$key][$k], $k.':00-'.$k.':59');
}
}
}
}else{
if($type == 3 || $type ==4){
$anew = [];
$anew[0] = $result[1];
foreach ($anew as $key => $value) {
foreach ($value as $k => $v) {
$count = count($value);
$anew[$key][$k][0] = date('Y/m/d', strtotime('-'.($count-$k-1).' day'));
$anew[$key][$k][1] = $v[0];
if($k<=10){
array_push($anew[$key][$k], $k.':00-'.$k.':59');
}else{
array_push($anew[$key][$k], $k.':00-'.$k.':59');
}
array_push($anew[$key][$k],$this->secToTime($v[0]));
}
}
return json_encode(['code'=>0, 'msg'=> Lang::get('查询成功'),'name'=>$name,'data'=>$anew]);
}else{
foreach ($new as $key => $value) {
foreach ($value as $k => $v) {
if($k<=10){
array_push($new[$key][$k], $k.':00-'.$k.':59');
}else{
array_push($new[$key][$k], $k.':00-'.$k.':59');
}
array_push($new[$key][$k],$this->secToTime($v[1]));
}
}
}
}
return jsonReturn(0, Lang::get('查询成功'),['name'=>$name,'data'=>$new]);
}
// /**
// * 关键词消费排名
// * @throws BadSysSettingException
// */
// public function search_word()
// {
// $type = $this->request->request('type')?$this->request->request('type'):1;
// $se = $this->getSTAndET($type);
// $data = [
// "site_id"=> $this->request->param('site_id')?$this->request->param('site_id'):$this->site_id,
// "start_date" => $se['st'],
// "end_date" => $se['st'],
// "metrics" => "pv_count,visit_count,visitor_count",
// "method" => "overview/getWord"
// ];
// $response = $this->getResponse($data);
// $response = $response['body']['data']['0']['result'];
// return jsonReturn(0, '查询成功',$response);
// }
/**
* Top10搜索词
* @throws BadSysSettingException
*/
public function search_word()
{
$type = $this->request->request('type')?$this->request->request('type'):1;
$se = $this->getSTAndET($type);
$data = [
"site_id"=> $this->request->param('site_id')?$this->request->param('site_id'):$this->site_id,
"start_date" => $se['st'],
"end_date" => $se['st'],
"metrics" => "pv_count,pv_ratio",
"method" => "source/searchword/a"
];
$response = $this->getResponse($data);
$response = $response['body']['data']['0']['result'];
$list = $response['items'][0] ?? [];
if (count($list) > 10) {
$list = array_slice($list, 0, 10);
$response['items'][0] = $list;
}
return jsonReturn(0, Lang::get('查询成功'),$response);
}
public function https_request($url,$data)
{
// 初始化
$curl = curl_init();
// 设置
curl_setopt($curl,CURLOPT_URL,$url);
// 检查ssl证书
curl_setopt($curl,CURLOPT_SSL_VERIFYPEER,FALSE);
// 从检查本地证书检查是否ssl加密
curl_setopt($curl,CURLOPT_SSL_VERIFYHOST,$url);
// 判断$data 判断是否post
if ( !empty($data) ) {
curl_setopt($curl,CURLOPT_POST,1);// 开启post
curl_setopt($curl,CURLOPT_POSTFIELDS,$data);// 发送post $data
}
// 返回结果 是文件流的方式返回
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if($err){
return false;
}else{
return $response;
}
}
/**
* 网站概况(来源网站、搜索词、入口页面、受访页面、新老访客)
* @throws BadSysSettingException
*/
public function get_common_track_rpt(): \think\response\Json
{
$type = $this->request->request('type') ? $this->request->request('type') : 1;
$se = $this->getSTAndET($type);
$data = [
"site_id"=> $this->request->param('site_id')?$this->request->param('site_id'):$this->site_id,
"start_date" => $se['st'],
"end_date" => $se['et'],
"method" => "overview/getCommonTrackRpt"
];
$response = $this->getResponse($data);
$response = $response['body']['data']['0']['result'];
foreach ($response['visitType'] as $key => $value) {
$response['visitType'][$key]['avg_visit_time'] = $this->secToTime($value['avg_visit_time']);
}
return jsonReturn(0, Lang::get('查询成功'), $response);
}
/**
* 网站概况(地域分布)
* @throws BadSysSettingException
*/
public function area()
{
$type = $this->request->request('type')?$this->request->request('type'):2;
$se = $this->getSTAndET($type);
$data = [
"site_id"=> $this->request->param('site_id')?$this->request->param('site_id'):$this->site_id,
"start_date" => $se['st'],
"end_date" => $se['et'],
"metrics"=> "pv_count,pv_ratio",
"method" => "visit/district/a"
];
$response = $this->getResponse($data);
$response = $response['body']['data']['0']['result'];
$new = [];
foreach ($response['items'][0] as $key => $value) {
$new[$key]['name'] = $value[0]['name'];
$new[$key]['area'] = $value[0]['area'];
}
foreach ($response['items'][1] as $key => $value) {
$new[$key]['pv_count'] = $value[0];
$new[$key]['pv_ratio'] = $value[1];
}
return jsonReturn(0, Lang::get('查询成功'),$new);
}
/**
*
* 网站概况(地域分布Top10)
*
* @throws BadSysSettingException
*/
public function area_top(): \think\response\Json
{
$type = $this->request->request('type')?$this->request->request('type'):2;
$se = $this->getSTAndET($type);
$data = [
"site_id"=> $this->request->param('site_id')?$this->request->param('site_id'):$this->site_id,
"start_date" => $se['st'],
"end_date" => $se['et'],
"metrics"=> "pv_count,pv_ratio",
"method" => "visit/district/a"
];
$response = $this->getResponse($data);
$response = $response['body']['data']['0']['result'];
$new = [];
foreach ($response['items'][0] as $key => $value) {
$new[$key]['name'] = $value[0]['name'];
$new[$key]['area'] = $value[0]['area'];
}
foreach ($response['items'][1] as $key => $value) {
$new[$key]['pv_count'] = $value[0];
$new[$key]['pv_ratio'] = $value[1];
}
$new = array_slice($new, 0, 10);
return jsonReturn(0, Lang::get('查询成功'), $new);
}
public function http_request($url,$data)
{
// 初始化
$curl = curl_init();
// 设置
curl_setopt($curl,CURLOPT_URL,$url);
// 检查ssl证书
curl_setopt($curl,CURLOPT_SSL_VERIFYPEER,FALSE);
// 从检查本地证书检查是否ssl加密
curl_setopt($curl,CURLOPT_SSL_VERIFYHOST,$url);
// 判断$data 判断是否post
if ( !empty($data) ) {
curl_setopt($curl,CURLOPT_POST,1);// 开启post
curl_setopt($curl,CURLOPT_POSTFIELDS,$data);// 发送post $data
}
// 返回结果 是文件流的方式返回
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
return $response;
}
// 获取开始日期和结束日期
public function getSTAndET($type): array
{
$et = date('Ymd', time());
if($type==1){
$st = date('Ymd', time());
}elseif ($type==2) {
$st = date('Ymd', strtotime('-1 day'));
$et = date('Ymd', strtotime('-1 day'));
}elseif ($type==3) {
$st = date('Ymd', strtotime('-6 day'));
}else {
$st = date('Ymd', strtotime('-29 day'));
}
return ['st' => $st,'et'=>$et];
}
public function secToTime($sec)
{
if (is_numeric($sec)) {
$strSecond = gmstrftime('%H:%M:%S', $sec);
} else {
$strSecond = $sec;
}
return $strSecond;
}
}

View File

@ -0,0 +1,136 @@
<?php
declare (strict_types = 1);
namespace app\controller\backend;
use app\model\SysSetting;
use app\service\CacheService;
use app\validate\SysSettingValidate;
use think\exception\ValidateException;
use think\facade\Cache;
use think\facade\Lang;
class SysSettingController extends BaseController
{
protected $group = ['email','upload','sem','analysis','others','company'];
/**
* 显示资源列表
*
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function index(SysSetting $sysSetting): \think\response\Json
{
$where = [
'seller_id' => $this->admin['seller_id'],
'status' => 1,
];
$order = 'sort asc';
$sysSettingList = $sysSetting->getAllCustomArrayData($where,$order)['data'];
$sysSettingList = makeTree($sysSettingList);
return jsonReturn(0,Lang::get('成功'),$sysSettingList);
}
/**
* 保存新建的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function save(SysSetting $sysSetting): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
// 数据验证
try{
validate(SysSettingValidate::class)->scene('save')->check($param);
}catch(ValidateException $e){
return jsonReturn(-1, $e->getError());
}
$res = $sysSetting -> addCustomData($param);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 显示指定的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function read(SysSetting $sysSetting): \think\response\Json
{
$param = input('param.');
// 数据验证
try{
validate(SysSettingValidate::class)->scene('read')->check($param);
}catch(ValidateException $e){
return jsonReturn(-1, $e->getError());
}
$where = [
['group','=',$param['group']],
['seller_id','=',$this->admin['seller_id']],
];
$order = 'sort asc';
$res = $sysSetting->getAllCustomArrayData($where,$order)['data'];
$res = generate($res);
return jsonReturn(0,Lang::get('成功'),$res);
}
/**
* 保存更新的资源
* @return \think\response\Json
* @throws \app\exception\ModelException*@throws \ReflectionException
* @throws \ReflectionException
*/
public function update(SysSetting $sysSetting): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
if(empty($param['group'])){
return jsonReturn(-1,Lang::get('配置分组不能为空'));
}
if(!in_array($param['group'],$this->group)){
return jsonReturn(-2,Lang::get('配置分组类型错误'));
}
if($param['group'] == 'setting'){
Cache::delete('hc_company_'. $this->admin['seller_id']);
}
try {
validate(SysSettingValidate::class)->check($param);
} catch (\Exception $e) {
return jsonReturn(-1, $e->getMessage());
}
$where = [
'seller_id' => $this->admin['seller_id'],
];
foreach($param as $key => $val){
$whereVal = array_merge($where,['title'=>$key]);
$sysSetting -> updateSysSetting($whereVal,['value'=>$val]);
}
CacheService::deleteRelationCacheByObject($sysSetting);
return jsonReturn(0,Lang::get("保存成功"));
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* @throws \app\exception\ModelException
* @throws \ReflectionException
*/
public function emailClear(SysSetting $sysSetting): \think\response\Json
{
$sysSetting -> updateSysSetting(['seller_id' => $this->admin['seller_id'],'group'=>'email'],['value'=>'']);
CacheService::deleteRelationCacheByObject($sysSetting);
return jsonReturn(0,Lang::get("成功"));
}
public function clearCache(): \think\response\Json
{
Cache::clear();
return jsonReturn(0, Lang::get("成功"));
}
}

View File

@ -0,0 +1,537 @@
<?php
declare (strict_types=1);
namespace app\controller\backend;
use app\BaseController;
use app\exception\InstallException;
use app\model\Admin;
use app\model\Model;
use app\model\Route;
use app\model\Website;
use app\model\WebsiteLang;
use app\model\WebsiteServer;
use app\model\WebsiteSetting;
use app\service\ThemeService;
use app\validate\SystemInstallValidate;
use think\exception\ValidateException;
use think\facade\Db;
use think\facade\Log;
use think\facade\View;
use think\Validate;
class SystemInstallController extends BaseController
{
/**
* 显示资源列表
*
*/
public function index()
{
if (hcInstalled()) {
return redirect('/admin.php');
}
// 向huocms官网请求是否认证
$domain = request()->host();
if (!empty($domain)) {
$authRes = curlPost(config('system.auth_query_url'), ['domain' => $domain])['data'];
Log::info('auth_query_url:' . $authRes);
}
$year = date('Y', time());
$month = date('m', time());
$day = date('d', time());
View::assign(['year' => $year, 'month' => $month, 'day' => $day]);
return View::fetch(CMS_ROOT . 'data/install/install.html');
}
public function monitor(): string
{
// if (file_exists_case('data/conf/config.php')) {
// @unlink('data/conf/config.php');
// }
$data = [];
$data['phpversion'] = @phpversion();
$data['os'] = PHP_OS;
$tmp = function_exists('gd_info') ? gd_info() : [];
// $server = $_SERVER["SERVER_SOFTWARE"];
// $host = $this->request->host();
// $name = $_SERVER["SERVER_NAME"];
// $max_execution_time = ini_get('max_execution_time');
// $allow_reference = (ini_get('allow_call_time_pass_reference') ? '<font color=green>[√]On</font>' : '<font color=red>[×]Off</font>');
// $allow_url_fopen = (ini_get('allow_url_fopen') ? '<font color=green>[√]On</font>' : '<font color=red>[×]Off</font>');
// $safe_mode = (ini_get('safe_mode') ? '<font color=red>[×]On</font>' : '<font color=green>[√]Off</font>');
$err = 0;
if (empty($tmp['GD Version'])) {
$gd = '/system_file/install/image/success_icon.jpg';
$err++;
} else {
$gd = '/system_file/install/image/error_icon.png ' . $tmp['GD Version'];
}
if (class_exists('pdo')) {
$data['pdo'] = '/system_file/install/image/success_icon.jpg';
} else {
$data['pdo'] = '/system_file/install/image/error_icon.png';
$err++;
}
if (extension_loaded('pdo_mysql')) {
$data['pdo_mysql'] = '/system_file/install/image/success_icon.jpg';
} else {
$data['pdo_mysql'] = '/system_file/install/image/error_icon.png';
$err++;
}
if (extension_loaded('curl')) {
$data['curl'] = '/system_file/install/image/success_icon.jpg';
} else {
$data['curl'] = '/system_file/install/image/error_icon.png';
$err++;
}
if (extension_loaded('gd')) {
$data['gd'] = '/system_file/install/image/success_icon.jpg';
} else {
$data['gd'] = '/system_file/install/image/error_icon.png';
if (function_exists('imagettftext')) {
$data['gd'] .= '/system_file/install/image/error_icon.png';
}
$err++;
}
if (extension_loaded('mbstring')) {
$data['mbstring'] = '/system_file/install/image/success_icon.jpg';
} else {
$data['mbstring'] = '/system_file/install/image/error_icon.png';
if (function_exists('imagettftext')) {
$data['mbstring'] .= '/system_file/install/image/error_icon.png';
}
$err++;
}
if (extension_loaded('fileinfo')) {
$data['fileinfo'] = '/system_file/install/image/success_icon.jpg';
} else {
$data['fileinfo'] = '/system_file/install/image/error_icon.png';
$err++;
}
if (ini_get('file_uploads')) {
$data['upload_size'] = ini_get('upload_max_filesize');
} else {
$data['upload_size'] = '未开启';
$err++;
}
if (function_exists('session_start')) {
$data['session'] = '/system_file/install/image/success_icon.jpg';
} else {
$data['session'] = '/system_file/install/image/error_icon.png';
$err++;
}
if (version_compare(phpversion(), '7.2.0', '>=') && version_compare(phpversion(), '7.0.0', '<') && ini_get('always_populate_raw_post_data') != -1) {
$data['always_populate_raw_post_data'] = '未关闭';
$data['show_always_populate_raw_post_data_tip'] = true;
$err++;
} else {
$data['always_populate_raw_post_data'] = '已关闭';
}
$folders = [
realpath(CMS_ROOT . 'data') . DIRECTORY_SEPARATOR,
realpath(CMS_ROOT . 'runtime') . DIRECTORY_SEPARATOR,
realpath(CMS_ROOT . 'public/themes') . DIRECTORY_SEPARATOR,
realpath(CMS_ROOT . 'public/storage') . DIRECTORY_SEPARATOR,
realpath(CMS_ROOT . 'public/poster_preview') . DIRECTORY_SEPARATOR,
];
$newFolders = [];
foreach ($folders as $dir) {
$testDir = $dir;
sp_dir_create($testDir);
if (sp_testwrite($testDir)) {
$newFolders[$dir]['w'] = true;
} else {
$newFolders[$dir]['w'] = false;
$err++;
}
if (is_readable($testDir)) {
$newFolders[$dir]['r'] = true;
} else {
$newFolders[$dir]['r'] = false;
$err++;
}
if ($newFolders[$dir]['w'] && $newFolders[$dir]['r']) {
$newFolders[$dir]['icon'] = '/system_file/install/image/success_icon.jpg';
} else {
$newFolders[$dir]['icon'] = '/system_file/install/image/error_icon.png';
}
}
$data['err'] = $err;
$data['folders'] = $newFolders;
View::assign($data);
return View::fetch(CMS_ROOT . 'data/install/monitor.html');
}
public function configure(): string
{
return View::fetch(CMS_ROOT . 'data/install/configure.html');
}
public function create(): string
{
$param = request()->param();
if (empty($param['username'])) {
$param['username'] = 'admin@admin.com';
}
if (empty($param['password'])) {
$param['password'] = 'huocms.com';
}
$dbConfig = [
'type' => 'mysql',
// 连接名
'hostname' => $param['hostname'],
// 用户名
'username' => $param['username'],
// 密码
'password' => $param['password'],
// 端口
'hostport' => $param['hostport'],
// 数据库编码默认采用utf8mb4
'charset' => $param['charset'],
// 数据库表前缀
'prefix' => $param['prefix'],
];
$this->updateDbConfig($dbConfig);
$sql = "CREATE DATABASE IF NOT EXISTS `{$param['database']}` DEFAULT CHARACTER SET " . $param['charset'];
$db = Db::connect('install_db');
$db->execute($sql);
$dbConfig['database'] = $param['database'];
$this->exchangeEnv($dbConfig);
session('install.db_config', $dbConfig);
$sql = hcSplitSql(CMS_ROOT . '/data/install/install.sql', $dbConfig['prefix'], $dbConfig['charset']);
session('install.sql', $sql);
View::assign('sql_count', count($sql));
session('install.error', 0);
session('install.admin_info', [
'name' => $param['admin'],
'account' => $param['email'],
'password' => makePassword($param['admin_pass'])
]);
return View::fetch(CMS_ROOT . 'data/install/create.html');
}
public function install()
{
$dbConfig = session('install.db_config');
$sql = session('install.sql');
if (empty($dbConfig) || empty($sql)) {
return json([
'code' => -1,
'data' => '',
'msg' => '非法安装!'
]);
}
$sqlIndex = $this->request->param('sql_index', 0, 'intval');
$this->updateDbConfig($dbConfig);
$db = Db::connect('install_db');
if ($sqlIndex >= count($sql)) {
return json([
'code' => 200,
'data' => '',
'msg' => '安装完成!'
]);
}
$sqlToExec = $sql[$sqlIndex] . ';';
$result = sp_execute_sql($db, $sqlToExec);
if (!empty($result['error'])) {
return json([
'code' => -1,
'data' => [
'sql' => $sqlToExec,
'exception' => $result['exception']
],
'msg' => '安装失败!'
]);
} else {
return json([
'code' => 0,
'data' => $result,
'msg' => '安装成功!'
]);
}
}
public function exchangeEnv($dbConfig)
{
$envFile = CMS_ROOT . '.env';
$example = CMS_ROOT . '.example.env';
if (file_exists($example)) {
copy($example, $envFile);
} else {
touch($envFile);
$initArray = [
"APP_DEBUG = true",
"APP_HOST = http://localhost",
"",
"[APP]",
"DEFAULT_TIMEZONE = Asia/Shanghai",
"",
"[DATABASE]",
"TYPE = mysql",
"HOSTNAME = 127.0.0.1",
"DATABASE = huo_cms",
"USERNAME = root",
"PASSWORD = ",
"HOSTPORT = 3306",
"CHARSET = utf8mb4",
"DEBUG = true",
"PREFIX = hc_",
"",
"[LANG]",
"default_lang = zh",
];
file_put_contents($envFile, implode(PHP_EOL, $initArray));
}
$merge = [
'app_debug' => true,
'app_host' => request()->domain(),
];
$data = [];
foreach (array_merge($merge, $dbConfig) as $key => $val) {
$key = strtoupper($key);
$data[$key] = $val;
}
$this->updateEnv($envFile, $data);
}
public function updateEnv($envFile, $data = array())
{
$oldEnv = file($envFile);
if (!count($data)) {
return;
}
if (array_keys($data) === range(0, count($data) - 1)) {
return;
}
$pattern = '/([^\=]*)\=[^\n]*/';
$newEnv = [];
foreach ($oldEnv as $line) {
preg_match($pattern, $line, $matches);
if (!count($matches)) {
$newEnv[] = $line;
continue;
}
if (!key_exists(trim($matches[1]), $data)) {
$newEnv[] = $line;
continue;
}
$line = trim($matches[1]) . "={$data[trim($matches[1])]}\n";
$newEnv[] = $line;
}
$newContent = implode('', $newEnv);
file_put_contents($envFile, $newContent);
}
public function userCheck(): \think\response\Json
{
$param = request()->param();
try {
validate(SystemInstallValidate::class)->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
return jsonReturn(0, 'success');
}
public function testPass(): \think\response\Json
{
$param = request()->param();
$dbConfig = [
'type' => 'mysql',
// 连接名
'hostname' => $param['hostname'],
// 数据库名
'username' => $param['username'],
// 密码
'password' => $param['password'],
// 端口
'hostport' => $param['hostport'],
];
$this->updateDbConfig($dbConfig);
$supportInnoDb = false;
try {
$db = Db::connect('install_db');
$engines = $db->query("SHOW ENGINES;");
foreach ($engines as $engine) {
if ($engine['Engine'] == 'InnoDB' && $engine['Support'] != 'NO') {
$supportInnoDb = true;
break;
}
}
} catch (\Exception $e) {
return jsonReturn(-1, '数据库账号或密码不正确!' . $e->getMessage());
}
if (!$supportInnoDb) {
return jsonReturn(-2, '数据库账号密码验证通过但不支持InnoDb!');
} else {
return jsonReturn(0, 'success');
}
}
public function installCompleteCheck(): \think\response\Json
{
$admin = session("install.admin_info");
if (empty($admin)) {
return jsonReturn(-1, '用户创建失败,请重试');
}
Db::startTrans();
try {
// 创建管理员
$Admin = new Admin();
$admin['seller_id'] = 1;
$admin['status'] = 1;
$admin['group'] = 1;
$res = $Admin->addAdmin($admin)['data'];
$res->role()->attach(1, ['seller_id' => 1]);
session('adminId', $res['id']);
$website = new Website();
$siteParam = [
'title' => '演示站点',
'domain' => request()->host()
];
// 创建站点
// 1. 保存站点到数据库
$res = $website->addWebsite($siteParam);
if ($res['data'] != 1) {
throw new InstallException();
}
// 2. 生成一份默认的站点配置,配置的值为空
$tmpData = config('system.website_setting');
$sysData = [
"setting" => json_encode($tmpData),
"website_id" => $res['data'],
"lang" => "zh"
];
$siteSetting = new WebsiteSetting();
$siteSetting->addWebsiteSetting($sysData);
// 3. 选择站点语言简体中文,服务器为本地服务器
$langData[] = [
'seller_id' => 1,
'website_id' => $res['data'],
'lang_name' => '简体中文',
'lang' => 'zh',
];
$WebsiteLang = new WebsiteLang();
$WebsiteLang->addWebsiteLang($langData);
$serverData = [
'seller_id' => 1,
'website_id' => $res['data'],
'type' => 1,
];
$WebsiteServer = new WebsiteServer();
$WebsiteServer->addWebsiteServer($serverData);
// 4.生成路由
$Route = new Route();
$Route->updateRoute(['seller_id' => 1], ['website_id' => $res['data']]);
$Route->getRoutes($res['data'], 1, 'zh');
// 5.安装模版
$ThemeService = new ThemeService();
$ThemeService->installTheme('demo', $res['data'], 1);
createInstallFile();
session("install.step", 4);
Db::commit();
} catch (InstallException $e) {
@unlink(CMS_ROOT . 'public/system_file/install.lock');
Db::rollback();
return jsonReturn(-3, '安装失败,请清空数据库后重试');
} catch (\Exception $e) {
@unlink(CMS_ROOT . 'public/system_file/install.lock');
Db::rollback();
return jsonReturn(-1, '用户创建失败,请重试' . $e->getMessage());
}
return jsonReturn(0, 'success');
}
public function completeCheck(): \think\response\Json
{
if (session("install.step") == 4) {
return jsonReturn(0, '安装成功');
} else {
return jsonReturn(-1, '安装失败');
}
}
public function complete(): string
{
return View::fetch(CMS_ROOT . 'data/install/complete.html');
}
private function updateDbConfig($dbConfig)
{
$oldDbConfig = config('database');
$oldDbConfig['connections']['install_db'] = $dbConfig;
config($oldDbConfig, 'database');
}
public function write_ini_file($assoc_arr, $path, $has_sections = FALSE): bool
{
$content = "";
if ($has_sections) {
foreach ($assoc_arr as $key => $elem) {
$content .= "[" . $key . "]n";
foreach ($elem as $key2 => $elem2) {
if (is_array($elem2)) {
for ($i = 0; $i < count($elem2); $i++) {
$content .= $key2 . "[] = " . $elem2[$i] . "\r\n";
}
} else if ($elem2 == "") {
$content .= $key2 . " = n";
} else {
$content .= $key2 . " = " . $elem2 . "\r\n";
}
}
}
} else {
foreach ($assoc_arr as $key => $elem) {
if (is_array($elem)) {
for ($i = 0; $i < count($elem); $i++) {
$content .= $key . "[] = " . $elem[$i] . "\r\n";
}
} else if ($elem == "") {
$content .= $key . " = n";
} else {
$content .= $key . " = " . $elem . "\r\n";
}
}
}
if (!$handle = fopen($path, 'w')) {
return false;
}
if (!fwrite($handle, $content)) {
return false;
}
fclose($handle);
return true;
}
}

View File

@ -0,0 +1,169 @@
<?php
declare (strict_types = 1);
namespace app\controller\backend;
use app\exception\ModelException;
use app\model\RecycleBin;
use app\model\Tag;
use app\validate\TagValidate;
use Overtrue\Pinyin\Pinyin;
use think\exception\ValidateException;
use think\facade\Lang;
class TagController extends BaseController
{
/**
* 显示资源列表
*
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function index(Tag $tag): \think\response\Json
{
$websiteId = (int)input('param.website_id');
if(!$websiteId){
return jsonReturn(-1,Lang::get('网站ID不能为空'));
}
$lang = input('lang');
if(empty($lang)){
$lang = 'zh';
}
$limit = $this->setLimit();
$title = input('title') ?: '';
$where = [
['seller_id', '=' ,$this->admin['seller_id']],
['website_id' ,'=', $websiteId],
['lang','=',$lang],
['is_del','=',1],
];
if($title){
array_push($where,['title','like','%'.$title.'%']);
}
try{
$tagList = $tag->where($where)->paginate($limit)->each(function(&$item){
$contentNum = $item->subContent()->where('is_del',1)->count();
$item -> article_count = $contentNum;
$item -> save();
});
}catch(\Exception $e){
throw new ModelException($e->getMessage());
}
return json(['code'=>0,'total'=>$tagList->total(),'msg'=>Lang::get('成功'),'data'=>$tagList->all()]);
}
/**
* 保存新建的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \app\exception\ModelNotUniqueException
*/
public function save(Tag $tag): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
// 数据验证
try{
validate(TagValidate::class)->scene('save')->check($param);
}catch(ValidateException $e){
return jsonReturn(-1, $e->getError());
}
$param['seller_id'] = $this->admin['seller_id'];
$param['unique_tag'] = $param['seller_id'] . '-' . $param['website_id'] . '-' . $param['lang'] .'-' . $param['title'];
$tag->saveUnique(['unique_tag'=> $param['unique_tag'],'is_del'=>1],Lang::get('标签名称已经存在'));
$pinyin = new Pinyin();
$param['first_letter'] = strtoupper(substr($pinyin->abbr($param['title']),0,1));
$param = $this->setSEO($param);
$res = $tag -> addTag($param);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 保存更新的资源
* @return \think\response\Json
* @throws \app\exception\ModelException*@throws \app\exception\ModelNotUniqueException
* @throws \app\exception\ModelNotUniqueException
*/
public function update(Tag $tag): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
try {
validate(TagValidate::class)->scene('update')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
$where = [
'id' => $param['id'],
'seller_id' => $this->admin['seller_id'],
'is_del' => 1,
];
$param = $this->setSEO($param);
$param['unique_tag'] = $this->admin['seller_id'] . '-' . $param['website_id'] . '-' . $param['lang'] .'-'. $param['title'];
$tag->updateUnique(['unique_tag' => $param['unique_tag']],$param['id'],Lang::get('标签名称已经存在'));
$pinyin = new Pinyin();
$param['first_letter'] = strtoupper(substr($pinyin->abbr($param['title']),0,1));
$res = $tag -> updateTag($where,$param);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 删除指定资源
*
* @throws \app\exception\ModelException
* @throws \app\exception\ModelEmptyException
*/
public function delete(Tag $Tag,RecycleBin $recycleBin): \think\response\Json
{
if(request()->isPost()){
$id = (int)input('id');
if(!$id){
return jsonReturn(-1,Lang::get('标签ID不能为空'));
}
$where = [
'id' => $id,
'seller_id' => $this->admin['seller_id'],
];
$tag = $Tag->getTag($where)['data'];
// 复制删除内容到回收站表
$binData = [
'seller_id' => $this->admin['seller_id'],
'object_id' => $id,
'sub_id' => '',
'module_id' => '',
'table_name' => 'tag',
'title' => $tag['title'],
'admin_id' => $this->admin['seller_id'],
'name' => $this->admin['name'],
];
$recycleBin -> addRecycleBin($binData);
$res = $Tag -> softDelTag($where);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 配置SEO
* @param $param
* @return mixed
*/
public function setSEO($param)
{
if(empty($param['seo_title'])){
$param['seo_title'] = $param['title'];
}
if(empty($param['seo_keywords'])){
$param['seo_keywords'] = $param['title'];
}
if(empty($param['seo_description'])){
$param['seo_description'] = $param['title'];
}
return $param;
}
}

View File

@ -0,0 +1,295 @@
<?php
declare (strict_types = 1);
namespace app\controller\backend;
use app\exception\ModelEmptyException;
use app\exception\ModelException;
use app\model\Theme;
use app\model\ThemeFile;
use app\service\ThemeService;
use app\validate\ThemeValidate;
use think\exception\ValidateException;
use think\facade\Db;
use think\facade\Lang;
use think\response\Json;
class ThemeController extends BaseController
{
/**
* 已安装模板
*
* @return Json
* @throws ModelException
*/
public function index(Theme $theme): Json
{
$website_id = (int)input('param.website_id');
if(!$website_id){
return jsonReturn(-1,Lang::get('网站ID不能为空'));
}
$lang = input('param.lang') ?: config('lang.default_lang');
$where = [
['seller_id','=',$this->admin['seller_id']],
['website_id','=',$website_id],
['lang','=',$lang]
];
$limit = $this->setLimit();
$themeList = $theme->getThemeList($where,$limit);
return json(pageReturn($themeList));
}
/**
* 模板完整更新
*
* @return Json
* @throws ModelException*@throws \app\exception\ModelEmptyException
* @throws ModelEmptyException
*/
public function update(ThemeService $themeService): Json
{
if(request()->isPost()){
$param = input('post.');
try {
validate(ThemeValidate::class)->scene('update')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
$suffix = config('view.view_suffix');
return $themeService->updateTheme($param['theme_id'],$param['website_id'],$this->admin['seller_id'],$suffix);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 系统模版
*
* @param Theme $themeModel
* @return Json
*/
public function install(Theme $themeModel): Json
{
$themesDirs = hcScanDir("themes/hc_original/*", GLOB_ONLYDIR);
$themes = [];
foreach ($themesDirs as $dir) {
$manifest = "themes/hc_original/$dir/manifest.json";
if (hcFileExist($manifest)) {
$manifest = file_get_contents($manifest);
$theme = json_decode($manifest, true);
}
$theme['theme'] = $dir;
$themes[] = $theme;
}
return jsonReturn(0,Lang::get('成功'),$themes);
}
/**
* 模版安装
*
* @throws ModelException
*/
public function installTheme(ThemeService $service): Json
{
if ($this->request->isPost()) {
set_time_limit(300);
$param = input('post.');
try {
validate(ThemeValidate::class)->scene('installTheme')->check($param);
}catch (ValidateException $e){
return jsonReturn(-1,$e->getError());
}
$suffix = config('view.view_suffix');
return $service->installTheme($param['theme'],$param['website_id'],$this->admin['seller_id'],$suffix,$param['lang']);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 卸载模板
* @throws ModelEmptyException
* @throws ModelException
*/
public function uninstall(ThemeService $service): Json
{
if ($this->request->isPost()) {
$param = input('post.');
try {
validate(ThemeValidate::class)->scene('uninstall')->check($param);
}catch (ValidateException $e){
return jsonReturn(-1,$e->getError());
}
return $service->uninstallTheme($param['theme_id'],$param['website_id'],$this->admin['seller_id']);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 模板文件
*
* @throws ModelException
*/
public function tmpFile(ThemeFile $ThemeFile): Json
{
$param = input('param.');
try {
validate(ThemeValidate::class)->scene('tmpFile')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
$limit = $this->setLimit();
$where = [
'seller_id' => $this->admin['seller_id'],
'theme_id' => $param['theme_id'],
'website_id' => $param['website_id'],
];
$res = $ThemeFile -> getThemeFileList($where,'id,name,file,real_path',$limit);
return json(pageReturn($res));
}
/**
*
* @param ThemeFile $ThemeFile
* @return Json
* @throws ModelException
*/
public function templateFile(ThemeFile $ThemeFile): Json
{
$param = input('param.');
try {
validate(ThemeValidate::class)->scene('templateFile')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
$theme = new Theme();
$activeTheme = $theme->getActiveTheme(['seller_id'=>$this->admin['seller_id'],'website_id'=>$param['website_id'],'lang'=>$param['lang'],'is_active'=>1])['data'];
if(empty($activeTheme)){
return jsonReturn(-1,Lang::get('未安装或启用模版'));
}
$where = [
'seller_id' => $this->admin['seller_id'],
'theme_id' => $activeTheme['id'],
'website_id' => $param['website_id'],
];
$template = $ThemeFile -> getAllCustomArrayData($where)['data'];
$data = [];
foreach ($template as $val){
$file = basename($val['file']);
if($param['type'] == 1){
if(preg_match('/^list_/',$file) || $file == 'index.html'){
$data[] = $val;
}
}else if($param['type'] == 2){
if(preg_match('/^detail/',$file)){
$data[] = $val;
}
}else if($param['type'] == 3){
if(preg_match('/^single_/',$file) || $file == 'index.html'){
$data[] = $val;
}
}else{
$data[] = $val;
}
}
return jsonReturn(0,Lang::get('成功'),$data);
}
/**
* 启用模版
*
* @throws ModelException
*/
public function active(Theme $Theme): Json
{
if(request()->isPost()){
$param = input('post.');
try {
validate(ThemeValidate::class)->scene('active')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
Db::startTrans();
try{
// 将所有模版暂停启用
$Theme->updateTheme(['seller_id'=>$this->admin['seller_id'],'website_id'=>$param['website_id'],'lang'=>$param['lang']],['is_active'=>2]);
// 启用当前模版
$res = $Theme->updateTheme(['id'=>$param['theme_id'],'seller_id'=>$this->admin['seller_id'],'website_id'=>$param['website_id'],'lang'=>$param['lang']],['is_active'=>1]);
Db::commit();
}catch (\Exception $e){
Db::rollback();
return jsonReturn(-3,Lang::get('模版安装启用错误'));
}
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 所有目录
* @throws ModelEmptyException
* @throws ModelException
*/
public function allFilePath(ThemeService $service): Json
{
$param = input('param.');
try {
validate(ThemeValidate::class)->scene('allFilePath')->check($param);
}catch (ValidateException $e){
return jsonReturn(-1,$e->getError());
}
return $service->filesPath($param['website_id'],$param['lang']);
}
public function allFiles(ThemeService $service): Json
{
$param = input('param.');
try {
validate(ThemeValidate::class)->scene('allFiles')->check($param);
}catch (ValidateException $e){
return jsonReturn(-1,$e->getError());
}
return $service->files($param['path'],(int)$param['website_id'],$param['lang']);
}
public function dealWithImg(): Json
{
$sourcePath = input('post.source');
$terminalPath = input('post.terminal');
$sourceFiles = glob($sourcePath.'/*');
$tmpFiles = [];
foreach ($sourceFiles as $sourceFile) {
$key = pathinfo($sourceFile,PATHINFO_FILENAME);
$ext = pathinfo($sourceFile,PATHINFO_EXTENSION);
$tmpFiles[$key] = $ext;
}
$terminalFiles = glob($terminalPath.'/*');
$termFiles = [];
foreach ($terminalFiles as $val) {
$key = pathinfo($val,PATHINFO_FILENAME);
$ext = pathinfo($val,PATHINFO_EXTENSION);
$termFiles[$key] = $ext;
}
foreach ($termFiles as $key => $term){
if($term != $tmpFiles[$key]){
$formName = $terminalPath .'/' . $key . '.' . $term;
$toName = $terminalPath.'/' . $key . '.' . $tmpFiles[$key];
rename($formName,$toName);
}
}
return jsonReturn(0,'success');
}
public function delete(ThemeService $service): Json
{
$theme = input('param.theme');
if(empty($theme)){
return jsonReturn(-1,Lang::get('模版名称不能为空'));
}
$res = $service -> delOriginalTheme($theme);
return json($res);
}
}

View File

@ -0,0 +1,206 @@
<?php
declare (strict_types = 1);
namespace app\controller\backend;
use app\exception\ModelEmptyException;
use app\model\Theme;
use app\model\ThemeFile;
use app\service\CacheService;
use app\service\ComponentDataService;
use app\service\ThemeFileService;
use app\validate\ThemeFileValidate;
use think\exception\ValidateException;
use think\facade\Lang;
class ThemeFileController extends BaseController
{
/**
* 显示资源列表
*
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function index(ThemeFile $themeFile): \think\response\Json
{
$where = [
'seller_id' => $this->admin['seller_id'],
'is_del' => 1,
];
$limit = $this->setLimit();
$themeFileList = $themeFile->getThemeFileList($where,$limit);
return json(pageReturn($themeFileList));
}
/**
* 保存新建的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \ReflectionException
*/
public function save(ThemeFile $themeFile): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
// 数据验证
try{
validate(ThemeFileValidate::class)->scene('save')->check($param);
}catch(ValidateException $e){
return jsonReturn(-1, $e->getError());
}
// TODO
// 其他逻辑
$res = $themeFile -> addThemeFile($param);
CacheService::deleteRelationCacheByObject($themeFile);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 显示指定的资源
*
* @return \think\response\Json
*/
public function read(ThemeFileService $themeFileService): \think\response\Json
{
$path = input('param.path');
if (strpos($path, '..') !== false) {
return jsonReturn(-1,Lang::get('文件路径有误'));
}
if(empty($path)){
return jsonReturn(-1,Lang::get('文件路径不能为空'));
}
// 不是可编辑文件
$ext = pathinfo($path, PATHINFO_EXTENSION);
if (!in_array($ext, ['js', 'json', 'html', 'css', 'less', 'htm', 'sass'])) {
return jsonReturn(-1,Lang::get('不支持的编辑文件'));
}
return $themeFileService->readSource($path);
}
/**
* 保存更新的资源
* @return \think\response\Json
* @throws \ReflectionException
*/
public function update(ThemeFileService $themeFileService): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
try {
validate(ThemeFileValidate::class)->scene('update')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
return $themeFileService -> updateResource($param['path'],$param['content']);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 更新单个文件
* @param ThemeFileService $themeFileService
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \ReflectionException
*/
public function singleUpdate(ThemeFileService $themeFileService): \think\response\Json
{
if(request()->isPost()){
$path = input('param.path');
$path = str_replace('\\', '/', $path);
$path = trim($path,'/');
if(empty($path)){
return jsonReturn(-1,Lang::get('文件路径不能为空'));
}
$res = $themeFileService -> updateThemeFile($path);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 模板设计
* @throws \app\exception\ModelEmptyException
* @throws \app\exception\ModelException
*/
public function design(ThemeFileService $themeFileService)
{
$param = input('param.');
try {
validate(ThemeFileValidate::class)->scene('design')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
$url = '/';
if(!empty($param['url'])){
$url = $param['url'];
}
define('ADMIN_SITE_ID',$param['website_id']);
define('ADMIN_SELLER_ID',$this->admin['seller_id']);
return $themeFileService -> design($param['theme_id'],$param['website_id'],$this->admin['seller_id'],$url);
}
/**
* @throws ModelEmptyException
* @throws \app\exception\ModelException
*/
public function compData(ThemeFileService $themeFileService)
{
$param = input('param.');
try {
validate(ThemeFileValidate::class)->scene('design')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
define('ADMIN_SITE_ID',$param['website_id']);
define('ADMIN_SELLER_ID',$this->admin['seller_id']);
define('ADMIN_LANG',$param['lang']);
return $themeFileService -> compData($this->admin['seller_id'],$param['theme_id'],$param['website_id'],$param['url'],$param['block']);
}
/**
* @throws \app\exception\ModelException
*/
public function active(Theme $Theme,ThemeFile $ThemeFile): \think\response\Json
{
$siteId = (int)input('website_id');
$lang = input('lang');
if(empty($siteId)){
return jsonReturn(-1,Lang::get('站点ID不能为空'));
}
$themeWhere = [
'seller_id' => $this->admin['seller_id'],
'website_id' => $siteId,
'lang' => $lang,
'is_active' => 1
];
try{
$theme = $Theme->getTheme($themeWhere)['data'];
}catch (ModelEmptyException $e){
return jsonReturn(-1,Lang::get('该站点没有启用的模板'));
}
$fileWhere = [
'seller_id' => $this->admin['seller_id'],
'website_id' => $siteId,
'theme_id' => $theme['id'],
];
$list = $ThemeFile->getThemeAllFile($fileWhere);
return json($list);
}
public function singleUpdateFile()
{
}
}

View File

@ -0,0 +1,84 @@
<?php
declare (strict_types = 1);
namespace app\controller\backend;
use app\model\SysSetting;
use app\service\upload\Upload;
use think\facade\Lang;
use think\Request;
class UploadController extends BaseController
{
protected $type = [
'file' => 4,
'image' => 2,
'video' => 3,
'mp3' => 5,
'zip' => 1,
];
/**
* 保存新建的资源
*
* @param \think\Request $request
* @return \think\Response
* @throws \app\exception\ModelException
*/
public function save(Request $request): \think\Response
{
if(request()->isPost()){
$file = request()->file('file');
if(empty($file)){
return jsonReturn(-2,Lang::get('文件不能为空'));
}
$seller_id = $this->admin['seller_id'];
// 查看文件类型
$fileName = $file->getOriginalName();
$fileExt = $file->getOriginalExtension();
$file_type = fileFormat($fileName);
$fileType = $this->type[$file_type];
// 附件大小和类型验证
// 获取上传配置
$Settings = new SysSetting();
$uploadSetting = $Settings->getAllCustomArrayData(['parent_id'=>1,'group'=>'upload','status'=>1],'id desc','id,group,title,value')['data'];
$uploadSetting = getColumnForKeyArray($uploadSetting,'title');
$limitSize = $uploadSetting[$file_type.'_size']['value'] * 1024; // byte
$fileSize = $file->getSize(); // 单位byte
if($fileSize > $limitSize){
return jsonReturn(-1,Lang::get('文件过大,请修改上传限制或者替换小的文件'));
}
$extArr = explode(',',$uploadSetting[$file_type.'_ext']['value']);
if(!in_array($fileExt,$extArr)){
return jsonReturn(-2,Lang::get('文件格式错误,请重新上传'));
}
// 文件信息提取
$where = [
'seller_id' => $seller_id,
'group' => 'upload',
'title' => 'storage'
];
$place = new SysSetting();
$type = $place->getSysSetting($where)['data']->toArray()['value'];
$upload = new Upload();
$info = $upload->create($file,$seller_id, $type,$file_type);
if ($info) {
$uploadInfo = $upload->getUploadFileInfo();
if ($uploadInfo['code'] == 0) {
$uploadInfo['data']['type'] = $fileType;
$uploadInfo['data']['size'] = round($fileSize / 1024,2);
$uploadInfo['data']['mime_type'] = $fileExt;
}
return json($uploadInfo);
}else{
return jsonReturn(-1, Lang::get('上传失败请重新尝试'));
}
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
}

View File

@ -0,0 +1,287 @@
<?php
declare (strict_types = 1);
namespace app\controller\backend;
use app\model\Admin;
use app\model\Route;
use app\model\Website;
use app\model\WebsiteLang;
use app\model\WebsiteSetting;
use app\service\CacheService;
use app\service\SitemapService;
use app\service\WebsiteLangService;
use app\validate\WebsiteValidate;
use app\service\WebsiteService;
use think\facade\Db;
use think\exception\ValidateException;
use think\facade\Lang;
class WebsiteController extends BaseController
{
/**
* 显示资源列表
*
* @return \think\Response
* @throws \app\exception\ModelException
* @throws \app\exception\ModelEmptyException
*/
public function index(Website $website): \think\response\Json
{
$where = [
['seller_id','=', $this->admin['seller_id']],
];
$uid = $this -> admin['uid'];
$authSiteId = [];
if($uid != 1){
$admin = new Admin();
$adminInfo = $admin -> getAdmin(['seller_id' => $this->admin['seller_id'],'id' => $uid],['role.website'])['data'] -> toArray();
$authSite = array_column($adminInfo['role'],'website');
foreach($authSite as $val){
$authSiteId = array_merge($authSiteId,array_column($val,'id'));
}
$authSiteId = array_unique($authSiteId);
$where[] = ['id','in',$authSiteId];
}
$websiteList = $website->getAllCustomArrayData($where)['data'];
$websiteList = makeTree($websiteList);
return jsonReturn(0,Lang::get('成功'),$websiteList);
}
/**
* 保存新建的资源
*
* @return \think\Response
* @throws \app\exception\ModelException
* @throws \app\exception\ModelNotUniqueException
*/
public function save(Website $website): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
// 数据验证
try{
validate(WebsiteValidate::class)->scene('save')->check($param);
}catch(ValidateException $e){
return jsonReturn(-1, $e->getError());
}
$website->saveUnique(['seller_id'=>$this->admin['seller_id'],'title'=>$param['title']],'网站名称已存在');
$param['seller_id'] = $this->admin['seller_id'];
$param['status'] = 1;
Db::startTrans();
try{
// 1. 保存站点到数据库
$res = $website -> addWebsite($param);
if($param['parent_id'] == 0){
$this->selfData($res['data'],$param['domain'],$param['parent_id']);
}else{
$this->sonData($res['data'],$param['domain'],$param['parent_id']);
}
CacheService::deleteRelationCacheByObject($website);
Db::commit();
}catch(\Exception $e){
Db::rollback();
return jsonReturn(-5,$e->getMessage());
}
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* @throws \app\exception\ModelException
*/
public function selfData($siteId, $domain, $parentId)
{
// 2. 生成一份默认的站点配置,配置的值为空
$tmpData = config('system.website_setting');
$sysData = [
"setting"=> json_encode($tmpData),
"website_id"=>$siteId,
"lang"=>config('lang.default_lang')
];
$siteSetting = new WebsiteSetting();
$siteSetting -> addWebsiteSetting($sysData);
// 3. 选择站点语言简体中文,服务器为本地服务器
$langData[]= [
'seller_id' => $this->admin['seller_id'],
'website_id' => $siteId,
'lang_name' => '简体中文',
'lang' => config('lang.default_lang'),
];
$WebsiteLang = new WebsiteLang();
$WebsiteLang->addWebsiteLang($langData);
}
/**
* @throws \app\exception\ModelEmptyException
* @throws \app\exception\ModelException
*/
public function sonData($siteId, $domain, $parentId)
{
$website = new Website();
// 1. 写入父站点的路由
$parent = $website -> getWebsite(['id'=>$parentId,'seller_id'=>$this->admin['seller_id']])['data'];
$parentDomain = $parent['domain'];
$start = $parentId . '_' ;
$path = CMS_ROOT . 'route';
$files = glob("$path/$start*.php");
foreach ($files as $file){
$tmpStr = str_replace($parentDomain,$domain,file_get_contents($file));
$basename = str_replace($start,$siteId.'_',basename($file));
file_put_contents($path . DIRECTORY_SEPARATOR . $basename,$tmpStr);
}
// 父站点路由copy一份到子站点保存到数据库
$route = new Route();
$routes = $route -> getAllCustomArrayData(['seller_id'=>$this->admin['seller_id'],'website_id'=>$parentId])['data'];
foreach ($routes as &$val){
$val['website_id'] = $siteId;
$val['seller_id'] = $this->admin['seller_id'];
unset($val['id']);
unset($val['create_time']);
unset($val['update_time']);
}
unset($val);
$route->addAllCustomData($routes);
// 2. 写入语言+配置
$WebsiteLang = new WebsiteLang();
$parentLang = $WebsiteLang->getAllCustomArrayData(['id'=>$parentId,'seller_id'=>$this->admin['seller_id']])['data'];
$lang = array_column($parentLang,'lang');
$websiteLangService = new WebsiteLangService();
$websiteLangService->setSiteLang(['seller_id'=>$this->admin['seller_id'],'site_id'=>$siteId,'lang'=>$lang]);
}
/**
* 显示指定的资源
*
* @return \think\Response
* @throws \app\exception\ModelException
* @throws \app\exception\ModelEmptyException
*/
public function read(Website $website): \think\response\Json
{
$id = (int)input('id');
if(!$id){
return jsonReturn(-1,Lang::get('网站ID不能为空'));
}
$where = [
'id' => $id,
'seller_id' => $this->admin['seller_id']
];
$res = $website->getWebsite($where);
return json($res);
}
/**
* 设置网站状态
* @param Website $website
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function setStatus(Website $website)
{
if(!request()->isPost()){
return jsonReturn(-3,Lang::get('请求方法错误'));
}
$param = $this->request->only(['id' => 0, 'status' => 1]);
try {
validate(WebsiteValidate::class)->scene('setStatus')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
$where = [
'id' => $param['id'],
'seller_id' => $this->admin['seller_id'],
];
if ($param['status'] == 2) {
$childWhere = [
'parent_id' => $param['id'],
'seller_id' => $this->admin['seller_id'],
];
$website->updateWebsite($childWhere, ['status' => $param['status']]);
}
$res = $website->updateWebsite($where, ['status' => $param['status']]);
return json($res);
}
/**
* 保存更新的资源
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \app\exception\ModelEmptyException
* @throws \ReflectionException
*/
public function update(Website $website): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
try {
validate(WebsiteValidate::class)->scene('update')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
$where = [
'id' => $param['id'],
'seller_id' => $this->admin['seller_id'],
];
$site = $website->getWebsite($where)['data'];
if($param['domain'] != $site['domain']){
// 修改路由域名配置
$routePath = app()->getRootPath() . 'route'. DIRECTORY_SEPARATOR;
$fileArr = hcScanDir($routePath.'*');
foreach($fileArr as $val){
$siteId = $param['id'];
$pattern = '/^'.$siteId.'_/';
if(is_file($routePath . $val) && preg_match($pattern,$val)){
$conArr = str_replace($site['domain'],$param['domain'],file_get_contents($routePath.$val));
file_put_contents($routePath.$val,$conArr);
}
}
}
$res = $website -> updateWebsite($where,$param);
CacheService::deleteRelationCacheByObject($website);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 删除指定资源
*
*/
public function delete(WebsiteService $websiteService): \think\response\Json
{
if(request()->isPost()){
$id = (int)input('id');
if(!$id){
return jsonReturn(-1,Lang::get('网站ID不能为空'));
}
$res = $websiteService->deleteSite($id,$this->admin['seller_id']);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* xml文件
* @param SitemapService $sitemapService
* @return \think\response\Json
* @throws \app\exception\ModelEmptyException
* @throws \app\exception\ModelException
*/
public function sitemap(SitemapService $sitemapService): \think\response\Json
{
$siteId = (int)$this->request->param('website_id');
$sellerId = $this->admin['seller_id'];
$res = $sitemapService -> makeSitemap($siteId,$sellerId);
return json($res);
}
}

View File

@ -0,0 +1,86 @@
<?php
declare (strict_types = 1);
namespace app\controller\backend;
use app\model\Model;
use app\model\Website;
use app\model\WebsiteLang;
use app\model\WebsiteSetting;
use app\service\CacheService;
use app\service\WebsiteLangService;
use app\validate\WebsiteLangValidate;
use think\Cookie;
use think\exception\ValidateException;
use think\facade\Lang;
class WebsiteLangController extends BaseController
{
/**
* 显示资源列表
*
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function index(WebsiteLang $websiteLang): \think\response\Json
{
$siteId = (int)input('site_id');
if(!$siteId){
return jsonReturn(-1, Lang::get('站点ID不能为空'));
}
$where = [
'seller_id' => $this->admin['seller_id'],
'website_id' => $siteId,
];
$websiteLangList = $websiteLang->getAllCustomArrayData($where,'id asc');
return json($websiteLangList);
}
/**
* 保存新建的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \ReflectionException
*/
public function save(WebsiteLangService $websiteLangService): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
// 数据验证
try{
validate(WebsiteLangValidate::class)->scene('save')->check($param);
}catch(ValidateException $e){
return jsonReturn(-1, $e->getError());
}
$param['seller_id'] = $this->admin['seller_id'];
$res = $websiteLangService -> setSiteLang($param);
CacheService::deleteRelationCacheByObject(WebsiteLang::class);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 系统语言
* @return \think\response\Json
*/
public function system(): \think\response\Json
{
if(request()->isGet()){
$lang = config('system.lang');
return json(['code'=>0,'msg'=>'success','data'=>$lang]);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 切换语言
*/
public function changeLang(\think\Lang $lang,Cookie $cookie){
$val = $this->request->param('lang/s','');
$cookie->set($lang->getConfig()['cookie_var'], $val);
return jsonReturn(0,Lang::get('操作成功'));
}
}

View File

@ -0,0 +1,90 @@
<?php
declare (strict_types = 1);
namespace app\controller\backend;
use app\model\WebsiteServer;
use app\validate\WebsiteServerValidate;
use think\exception\ValidateException;
use think\facade\Lang;
class WebsiteServerController extends BaseController
{
/**
* 保存新建的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function save(WebsiteServer $websiteServer): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
// 数据验证
try{
validate(WebsiteServerValidate::class)->scene('save')->check($param);
}catch(ValidateException $e){
return jsonReturn(-1, $e->getError());
}
$param['seller_id'] = $this->admin['seller_id'];
$res = $websiteServer -> addWebsiteServer($param);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 显示指定的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \app\exception\ModelEmptyException
*/
public function read(WebsiteServer $websiteServer): \think\response\Json
{
$siteId = (int)input('site_id');
if(!$siteId){
// 修改错误消息
return jsonReturn(-1,Lang::get('网站ID不能为空'));
}
$where = [
'website_id' => $siteId,
'seller_id' => $this->admin['seller_id']
];
$res = $websiteServer->getWebsiteServer($where);
return json($res);
}
/**
* 保存更新的资源
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function update(WebsiteServer $websiteServer): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
try {
validate(WebsiteServerValidate::class)->scene('update')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
$where = [
'seller_id' => $this->admin['seller_id'],
'website_id' => $param['website_id'],
];
unset($param['website_id']);
$res = $websiteServer -> updateWebsiteServer($where,$param);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
}

View File

@ -0,0 +1,125 @@
<?php
declare (strict_types = 1);
namespace app\controller\backend;
use app\model\Attachment;
use app\model\Model;
use app\model\WebsiteSetting;
use app\validate\WebsiteSettingValidate;
use think\exception\ValidateException;
use think\facade\Cache;
use think\facade\Cookie;
use think\facade\Lang;
class WebsiteSettingController extends BaseController
{
/**
* 保存新建的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \app\exception\ModelNotUniqueException
*/
public function save(WebsiteSetting $websiteSetting): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
// 数据验证
try{
validate(WebsiteSettingValidate::class)->scene('save')->check($param);
}catch(ValidateException $e){
return jsonReturn(-1, $e->getError());
}
$param['seller_id'] = $this->admin['seller_id'];
$param['setting'] = json_encode($param['setting']);
$websiteSetting->saveUnique(['seller_id'=>$param['seller_id'],'website_id'=>$param['website_id'],'lang'=>$param['lang']],'相同配置已经存在,请编辑');
$res = $websiteSetting -> addWebsiteSetting($param);
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
/**
* 显示指定的资源
*
* @return \think\response\Json
* @throws \app\exception\ModelException
* @throws \app\exception\ModelEmptyException
*/
public function read(WebsiteSetting $websiteSetting,Attachment $attachment): \think\response\Json
{
$param = input('param.');
// 数据验证
try{
validate(WebsiteSettingValidate::class)->scene('read')->check($param);
}catch(ValidateException $e){
return jsonReturn(-1, $e->getError());
}
$where = [
'seller_id' => $this->admin['seller_id'],
'website_id' => $param['website_id'],
'lang' => $param['lang'],
];
$cacheKey = $this->admin['seller_id'] .'_'.$param['website_id'] .'_'. $param['lang'] . '_website_cache_key';
Cache::delete($cacheKey);
$res = $websiteSetting->getWebsiteSetting($where);
$res['data'] = $res['data']->toArray();
$res['data']['setting'] = json_decode($res['data']['setting'],true);
if(!empty($res['data']['setting']['logo'])){
$attach = $attachment->getAttachment(['id'=>$res['data']['setting']['logo'],'seller_id'=>$this->admin['seller_id']])['data'];
if(!empty($attach)){
$res['data']['setting']['logo'] = $attach;
}
}
if(!empty($res['data']['setting']['wechat_code'])){
$attach = $attachment->getAttachment(['id'=>$res['data']['setting']['wechat_code'],'seller_id'=>$this->admin['seller_id']])['data'];
if(!empty($attach)){
$res['data']['setting']['wechat_code'] = $attach;
}
}
if(!empty($res['data']['setting']['dark_logo'])){
$attach = $attachment->getAttachment(['id'=>$res['data']['setting']['dark_logo'],'seller_id'=>$this->admin['seller_id']])['data'];
if(!empty($attach)){
$res['data']['setting']['dark_logo'] = $attach;
}
}
return json($res);
}
/**
* 保存更新的资源
* @return \think\response\Json
* @throws \app\exception\ModelException
*/
public function update(WebsiteSetting $websiteSetting): \think\response\Json
{
if(request()->isPost()){
$param = input('post.');
try {
validate(WebsiteSettingValidate::class)->scene('update')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
$where = [
'seller_id' => $this->admin['seller_id'],
'website_id' => $param['website_id'],
'lang' => $param['lang'],
];
$setting = json_encode($param['setting']);
$res = $websiteSetting -> updateWebsiteSetting($where,['setting'=>$setting]);
$siteCacheKey = $this->admin['seller_id'] .'_'.$param['website_id'] .'_'. $param['lang'] . '_website_cache_key';
Cache::delete($siteCacheKey);
if(config('lang')['use_cookie']){
Cookie::set(config('lang')['cookie_var'],$param['lang']);
}
return json($res);
}
return jsonReturn(-3,Lang::get('请求方法错误'));
}
}

View File

@ -0,0 +1,322 @@
<?php
declare (strict_types = 1);
namespace app\controller\frontend;
use app\BaseController as AppBaseController;
use app\model\Theme;
use app\model\VisitLog;
use app\model\Website;
use app\service\ApiService;
use think\facade\Cache;
use think\facade\View;
class BaseController extends AppBaseController
{
protected $rootDomain;
protected $domain;
protected $siteId;
protected $sellerId;
protected $theme;
protected $lang;
protected $viewBase;
protected $settingData;
protected $parentSiteId;
protected $realSiteId;
protected $viewSiteId;
protected $preview;
/**
* @throws \app\exception\ModelException
* @throws \Exception
*/
public function initialize()
{
$this->setCommonParam();
$this->getRootDomain();
$this->setRealSiteId();
$this->getActiveTheme();
$this->setVisitedData();
parent::initialize();
}
/**
* @throws \app\exception\ModelException
*/
public function setCommonParam()
{
// 更新版本标识
updateVersion();
$domain = $this->request->host();
$Website = new Website();
$website = $Website->getWebsiteByDomain($domain,'id,domain,seller_id,parent_id,status')['data'];
if ($website['status'] == 2) {
$this->to404();
}
// 是否为预览状态
$historyViewTime = session('history-view');
$preview = $this->request->param('preview', '');
if (!empty($preview)) {
if ((time() - $historyViewTime) < 30 * 60) {
// 30分钟内可预览
$this->preview = 1;
} else {
header('Location: /');
return;
}
}
$this->domain = $domain;
$this->siteId = $website['id'];
$this->parentSiteId = $website['parent_id'];
$this->sellerId = $website['seller_id'];
$this->lang = $this->setLang();
$this->viewBase = config('view.view_dir_name');
define('INDEX_SITE_ID',$this->siteId);
define('INDEX_SELLER_ID',$this->sellerId);
define('INDEX_LANG',$this->lang);
}
public function getRootDomain()
{
$rootDomain = $this->request->rootDomain();
if( $rootDomain === 'com.cn'){
$domainArr = explode('.',$this->domain);
array_shift($domainArr);
$this->rootDomain = implode('.',$domainArr);
}else{
$this->rootDomain = $rootDomain;
}
}
/**
* @throws \app\exception\ModelException
* @throws \app\exception\ModelEmptyException
*/
public function getActiveTheme()
{
$Theme = new Theme();
$theme = $Theme->getActiveTheme(['seller_id'=>$this->sellerId,'website_id'=>$this->viewSiteId,'lang'=>$this->lang,'is_active'=>1])['data']->toArray();
$cacheKey = $this->sellerId .'_'.$this->siteId .'_'. $this->lang . '_website_cache_key';
$settingData = Cache::get($cacheKey);
if(empty($settingData)){
$settingData = ApiService::setWebsiteSetting($this->sellerId,$this->siteId,$this->lang);
}
$this->settingData = $settingData;
if(isMobile() && !empty($settingData['common_template']) && $settingData['common_template'] == 2){
$this->theme = 'm_' . $theme['theme'];
}else{
$this->theme = $theme['theme'];
}
}
public function setRealSiteId()
{
$siteId = $this->siteId;
$Theme = new Theme();
$theme = $Theme->getActiveTheme(['seller_id'=>$this->sellerId,'website_id'=>$siteId,'lang'=>$this->lang,'is_active'=>1])['data'];
$this->viewSiteId = $siteId;
// 本站点没有安装启用模板,页面加载用父级的模板
if ($this->parentSiteId && empty($theme)){
$this->viewSiteId = $this->parentSiteId;
}
if($this->parentSiteId){
$siteId = $this->parentSiteId;
}
define('INDEX_REAL_SITE_ID',$siteId);
return $this->realSiteId = $siteId;
}
public function setLang(): string
{
$baseUrl = trim($this->request->baseUrl(),'/');
$firstPath = explode('/',$baseUrl)[0];
$langArr = config('system.lang');
if(empty($firstPath) || !in_array($firstPath,$langArr)){
$firstPath = config('lang.default_lang');
}
return $firstPath;
}
protected function assign($name,$value = ''): \think\View
{
return View::assign($name, $value);
}
/**
* 加载模板输出
* @access protected
* @param string $template 模板文件名
* @param array $vars 模板输出变量
* @return mixed
*/
protected function fetch(string $template = '', array $vars = [])
{
$template = $this->parseTemplate($template);
$this->_initializeView($this->viewSiteId,$this->lang,$this->theme);
$html = View::fetch($template, $vars);
$replace = <<<EOL
<script id="baseJs" src="/system_file/js/base.min.js"></script>
</head>
EOL;
$html = str_replace('</head>', $replace, $html);
return $html;
}
/**
* 加载模板输出(system_file)
* @access protected
* @param string $template 模板文件名
* @param array $vars 模板输出变量
* @return mixed
*/
protected function fetchSystem(string $template = '', array $vars = [])
{
$template = $this->parseTemplateSystem($template);
$this->_initializeViewSystem($this->viewSiteId,$this->lang,$this->theme);
$html = View::fetch($template, $vars);
$replace = <<<EOL
<script id="baseJs" src="/system_file/js/base.min.js"></script>
</head>
EOL;
$html = str_replace('</head>', $replace, $html);
return $html;
}
/**
* 定位模板
* @param $template
* @return string
*/
private function parseTemplate($template): string
{
$path = CMS_ROOT . $this->viewBase . DIRECTORY_SEPARATOR . $this->viewSiteId.DIRECTORY_SEPARATOR .$this->lang.DIRECTORY_SEPARATOR.$this->theme;
if (!empty($this->preview)) {
$path = CMS_ROOT . 'public/themes/preview/'.$this->theme;
}
$ext = getFileExt($template);
$path .= DIRECTORY_SEPARATOR . ltrim($template,'/');
if( $ext != config('view.view_suffix')){
$path .= '.'.config('view.view_suffix');
}
return $path ;
}
/**
* 定位模板
* @param $template
* @return string
*/
private function parseTemplateSystem($template): string
{
$path = CMS_ROOT . 'public/system_file' . DIRECTORY_SEPARATOR;
$ext = getFileExt($template);
$path .= DIRECTORY_SEPARATOR . ltrim($template,'/');
if( $ext != config('view.view_suffix')){
$path .= '.'.config('view.view_suffix');
}
return $path ;
}
/**
* 常量替换
* @param $siteId
* @param $lang
* @param $themeName
*/
protected function _initializeView($siteId,$lang,$themeName)
{
if (!empty($this->preview)) {
$viewReplaceStr = [
'__TMPL__' => "/themes/preview/{$themeName}",
'__STATIC__' => empty($this->settingData['static_file']) ? "/themes/preview/{$themeName}/static" : $this->settingData['static_file'] ,
];
View::engine()->config([
'view_path' => CMS_ROOT . '/public/themes/preview/' .$this->theme . '/',
'tpl_replace_string' => $viewReplaceStr,
'display_cache' => false,
'tpl_cache' => false,
]);
} else {
$viewReplaceStr = [
'__TMPL__' => "/themes/website/{$siteId}/{$lang}/{$themeName}",
'__STATIC__' => empty($this->settingData['static_file']) ? "/themes/website/{$siteId}/{$lang}/{$themeName}/static" : $this->settingData['static_file'] ,
];
View::engine()->config([
'view_path' => CMS_ROOT . $this->viewBase . DIRECTORY_SEPARATOR . $siteId. DIRECTORY_SEPARATOR .$this->lang.DIRECTORY_SEPARATOR.$this->theme .DIRECTORY_SEPARATOR,
'tpl_replace_string' => $viewReplaceStr,
'display_cache' => false,
'tpl_cache' => false,
]);
}
}
/**
* 常量替换
* @param $siteId
* @param $lang
* @param $themeName
*/
protected function _initializeViewSystem($siteId,$lang,$themeName)
{
$viewReplaceStr = [
'__TMPL__' => "/themes/website/{$siteId}/{$lang}/{$themeName}",
'__STATIC__' => empty($this->settingData['static_file']) ? "/themes/website/{$siteId}/{$lang}/{$themeName}/static" : $this->settingData['static_file'] ,
];
View::engine()->config([
'view_path' => CMS_ROOT . 'public/system_file' . DIRECTORY_SEPARATOR,
'tpl_replace_string' => $viewReplaceStr,
'display_cache' => false,
'tpl_cache' => false,
]);
}
/**
* @throws \Exception
*/
private function setVisitedData()
{
$VisitLog = new VisitLog();
$ip = request()->ip();
$agent = !empty(request()->header()['user-agent']) ? request()->header()['user-agent'] : '未知设备';
$address = getLocationByIp($ip,2);
if(empty($address['province'])){
$area = "内网ip";
}else{
$area = $address['province']."-".$address['city'];
}
$url = $this->request->domain().$this->request->url();
$device = getDeviceInfo($agent);
$param = [
'seller_id' => $this->sellerId,
'website_id' => $this->siteId,
'domain' => $this->domain,
'referrer' => empty($_SERVER['HTTP_REFERER']) ? '未知' : $_SERVER['HTTP_REFERER'],
'url' => $url,
'ip' => $ip,
'agent' => $agent,
'visited_area' => $area,
'visited_device' => $device['deviceOs'] .'/'.$device['deviceVersion'],
'visited_time' => date('Y-m-d',time())
];
$VisitLog -> addCustomData($param);
}
public function to404()
{
header('Location: /system_file/hc_error/404.html');
exit();
}
}

View File

@ -0,0 +1,132 @@
<?php
namespace app\controller\frontend;
use think\facade\View;
class DesignBaseController extends BaseController
{
protected $designBase = 'public/design/designPage';
protected $designPath = '';
public function initialize()
{
parent::initialize();
$this->setPath();
}
public function setPath()
{
$this->designPath = CMS_ROOT . 'public/design/';
}
/**
* 加载模板输出(system_file)
* @access protected
* @param string $template 模板文件名
* @param array $vars 模板输出变量
* @return mixed
*/
protected function fetchDesign(string $template = '', array $vars = [])
{
$template = $this->parseTemplateDesign($template);
$this->_initializeViewDesign($this->realSiteId,$this->lang);
$html = View::fetch($template, $vars);
$replace = <<<EOL
<script id="baseJs" src="/system_file/js/base.min.js"></script>
</head>
EOL;
$html = str_replace('</head>', $replace, $html);
return $html;
}
/**
*
* 定位模板
* @param $template
* @return string
*/
private function parseTemplateDesign($template): string
{
$path = CMS_ROOT . "public/design/designPage" . DIRECTORY_SEPARATOR;
$ext = getFileExt($template);
$path .= DIRECTORY_SEPARATOR . ltrim($template,'/');
if( $ext != config('view.view_suffix')){
$path .= '.'.config('view.view_suffix');
}
return $path ;
}
/**
* 常量替换
* @param $siteId
* @param $lang
*/
protected function _initializeViewDesign($siteId,$lang)
{
$viewReplaceStr = [
'__TMPL__' => "/design/designPage",
'__STATIC__' => empty($this->settingData['static_file']) ? "/design/designPage/static" : $this->settingData['static_file'] ,
];
View::engine()->config([
'view_path' => CMS_ROOT . $this->designBase . DIRECTORY_SEPARATOR,
'tpl_replace_string' => $viewReplaceStr,
'display_cache' => false,
'tpl_cache' => false,
]);
}
/**
* 加载模板输出(system_file)
* @access protected
* @param string $template 模板文件名
* @param array $vars 模板输出变量
* @return mixed
*/
protected function fetchDesignPage(string $template = '', array $vars = [])
{
$template = $this->parseTemplateDesignPage($template);
$this->_initializeViewDesignPage($this->realSiteId,$this->lang);
return View::fetch($template, $vars);
}
/**
*
* 定位模板
* @param $template
* @return string
*/
private function parseTemplateDesignPage($template): string
{
$path = CMS_ROOT . "design/designPage/{$this->realSiteId}/{$this->lang}" . DIRECTORY_SEPARATOR;
$ext = getFileExt($template);
$path .= DIRECTORY_SEPARATOR . ltrim($template,'/');
if( $ext != config('view.view_suffix')){
$path .= '.'.config('view.view_suffix');
}
return $path ;
}
/**
* 常量替换
* @param $siteId
* @param $lang
*/
protected function _initializeViewDesignPage($siteId,$lang)
{
$viewReplaceStr = [
'__TMPL__' => "/design/designPage/{$siteId}/{$lang}",
'__STATIC__' => empty($this->settingData['static_file']) ? "/design/designPage/{$siteId}/{$lang}/static" : $this->settingData['static_file'] ,
];
View::engine()->config([
'view_path' => CMS_ROOT . $this->designBase . DIRECTORY_SEPARATOR . $siteId. DIRECTORY_SEPARATOR .$this->lang.DIRECTORY_SEPARATOR,
'tpl_replace_string' => $viewReplaceStr,
'display_cache' => false,
'tpl_cache' => false,
]);
}
}

View File

@ -0,0 +1,140 @@
<?php
namespace app\controller\frontend;
use app\model\BigField;
use app\model\Category;
use app\model\Theme;
use app\model\ThemeFile;
use app\model\WebsiteSetting;
use app\validate\DesignValidate;
use think\exception\ValidateException;
class DesignController extends DesignBaseController
{
public function index()
{
$param = $this->request->param();
$param['html'] = $param['html'] ?? '';
// 数据验证
try{
validate(DesignValidate::class)->scene('index')->check($param);
}catch(ValidateException $e){
return jsonReturn(-1, $e->getError());
}
// 获取当前站的模板目录
$themeModel = new Theme();
$theme = $themeModel->where([
['website_id', '=', $param['website_id']],
['lang', '=', $param['lang']],
['is_active', '=', 1],
])->value('theme');
if (empty($theme)) {
return jsonReturn(-1, lang('当前站点没有激活的模板,请去安装模板'));
}
//search for html files in demo and my-pages folders
// $htmlFiles = glob("{design/designPage/*\/*.html,design/designPage/*.html,design/demo/*\/*.html,design/demo/*.html}", GLOB_BRACE);
// $htmlFiles = glob("{design/designPage/*\/*.html,design/designPage/*.html}", GLOB_BRACE);
// $htmlFiles = glob("{themes/hc_original/{$theme}/*.html,themes/hc_original/{$theme}/*\/*.html}", GLOB_BRACE);
//
//
// $files = [];
// foreach ($htmlFiles as $file) {
// $file = mb_substr($file, 7);
// $pathInfo = pathinfo($file);
// if (in_array($pathInfo['basename'], array('new-page-blank-template.html', 'editor.html'))) continue;//skip template files
//
// $filename = $pathInfo['filename'];
//// var_dump($filename);
//
// $folder = preg_replace('@/.+?$@', '', $pathInfo['dirname']);
// $subfolder = preg_replace('@^.+?/@', '', $pathInfo['dirname']);
//
// if ($filename == 'index' && $subfolder) {
//// $filename = $subfolder;
// }
// $url = $pathInfo['dirname'] . '/' . $pathInfo['basename'];
// $name = ucfirst($filename);
//
//// dump($pathInfo['basename']);
//
// $files[] = [
// 'name' => $name,
// 'file' => $filename,
// 'title' => $name,
//// 'url' => '../themes/' . $url,
// 'url' => '/' . $pathInfo['basename'],
// 'folder' => '../themes/' . $folder,
// ];
//
//// $files .= "{name:'$name', file:'$filename', title:'$name', url: '$url', folder:'$folder'},";
// }
// 获取当前网站所有有模板的栏目的路由
$category = new Category();
$list = $category->where([
['website_id', '=', $param['website_id']],
['lang', '=', $param['lang']],
['type', 'in', [4]],
['status', '=', 1],
['list_tpl', '<>', ''],
])->field('id, title, list_tpl, alias')->group('list_tpl')->select()->toArray();
if (empty($list)) {
return jsonReturn(-1, lang('当前站点已安装模板,但没有栏目使用单页模板,如当前站是子站,需要编辑父站点的数据,请回后台切换至父站点'));
}
$files = [];
$initPage = '';
foreach ($list as $value) {
$pathInfo = pathinfo($value['list_tpl']);
if ($param['html'] == $value['list_tpl']) {
$initPage = $pathInfo['filename'];
}
$file = [
'name' => $pathInfo['filename'],
'file' => $value['list_tpl'],
'title' => $value['list_tpl'],
// 'url' => '../themes/' . $url,
'url' => $value['alias'],
'folder' => $value['title'],
];
$files[] = $file;
}
// dd($files);
$where = [
'website_id' => $param['website_id'],
'lang' => $param['lang'],
];
// 获取系统配置
$websiteSetting = new WebsiteSetting();
$res = $websiteSetting->getWebsiteSetting($where);
$res['data'] = $res['data']->toArray();
$setting = $res['data']['setting'];
$initPage = $initPage ?: $files[0]['name'];
$data = [
'website_id' => $param['website_id'],
'lang' => $param['lang'],
'setting' => $setting,
'pages' => json_encode($files),
'init_page' => $initPage,
];
$this->assign($data);
return $this->fetchDesign('editor');
}
public function getDesignHtml()
{
$param = $this->request->param();
return $this->fetchDesign($param['html_path']);
}
}

View File

@ -0,0 +1,77 @@
<?php
declare (strict_types = 1);
namespace app\controller\frontend;
use app\exception\ModelEmptyException;
use app\model\Category;
use app\model\SysSetting;
use app\service\ContentService;
use think\facade\Cache;
class DetailController extends BaseController
{
/**
* 内容详情
* @param Category $Category
* @return mixed
* @throws \app\exception\ModelEmptyException
* @throws \app\exception\ModelException
*/
public function index(Category $Category)
{
$id = (int)input('id');
$cid = (int)input('cid');
$cateCacheKey = 'hc_detail_cate_' .$this->sellerId.'_'.$this->siteId.'_'.$this->lang.'_'. $cid ;
$category = cache($cateCacheKey);
if(empty($category)){
try {
$category = $Category->getCategory(['id' => $cid, 'seller_id' => $this->sellerId, 'website_id' => $this->siteId,'lang' => $this->lang],['module.moduleField'])['data']->toArray();
Cache::set($cateCacheKey,$category);
}catch (ModelEmptyException $me){
$category = $Category->getCategory(['id' => $cid, 'seller_id' => $this->sellerId, 'website_id' => $this->realSiteId,'lang' => $this->lang],['module.moduleField'])['data']->toArray();
Cache::set($cateCacheKey,$category);
}
}
if (!empty($category['content'])) {
$category['content'] = htmlspecialchars_decode($category['content']);
}
$contentService = new ContentService();
$param = [
'cate' => $category,
'id' => $id,
'seller_id' => $this->sellerId,
'website_id' => $this->siteId,
'lang' => $this->lang
];
$content = $contentService->getContentDetail($param);
$content->hits = $content->hits + 1;
$content->save();
if (!empty($content['content'])) {
$content['content'] = htmlspecialchars_decode($content['content']);
}
$where = [
'group' => 'company',
'title' => 'huocms_powerby',
];
$huocmsPowerby = SysSetting::where($where)->value('value');
if ($huocmsPowerby) {
$category['seo_title'] .= " - $huocmsPowerby";
$category['seo_keywords'] .= "-$huocmsPowerby";
$category['seo_description'] .= ",$huocmsPowerby";
$content['seo_title'] .= "-$huocmsPowerby";
$content['seo_keyword'] .= ",$huocmsPowerby";
$content['seo_description'] .= ",$huocmsPowerby";
}
$this->assign('hc_content',$content->toArray());
$this->assign('current_cate',$category);
$this->assign('root_domain',$this->rootDomain);
$template = !empty($content['detail_tpl']) ? $content['detail_tpl'] : $category['detail_tpl'];
return $this->fetch($template);
}
}

View File

@ -0,0 +1,274 @@
<?php
namespace app\controller\frontend;
use app\model\Attachment;
use app\model\customModel\Down;
use app\model\DiyForm;
use app\model\SubContent;
use app\model\SysSetting;
use app\service\upload\Upload;
use think\facade\Db;
class FormController extends BaseController
{
protected $type = [
'file' => 4,
'image' => 2,
'video' => 3,
'mp3' => 5,
'zip' => 1,
];
public function index()
{
$code = $this->request->param('code');
$formModel = new DiyForm();
$formData = $formModel->where('code', $code)->findOrEmpty();
if (empty($formData)) {
$this->to404();
}
$this->assign('formData', $formData);
$html = $this->formHtml();
$html = str_replace(['{formData_name}', '{formData_designContent}', '{formData_designOption}', '{formData_code}'],
[$formData['name'],$formData['design_content'],$formData['design_option'],$formData['code']], $html);
return $html;
}
public function save()
{
$param = $this->request->param();
$code = $param['code'];
unset($param['code']);
$formModel = new DiyForm();
$formData = $formModel->where('code', $code)->findOrEmpty();
if (empty($formData)) {
return jsonReturn(-1, lang('提交失败'));
}
$tableName = 'diyform_' . $formData['table'];
foreach ($param as &$value) {
if (is_array($value)) {
$value = implode(',', $value);
}
}
$param['ip'] = $this->request->ip();
$param['user_agent'] = $this->request->header('user-agent');
Db::name($tableName)->insert($param);
return jsonReturn(0, lang('提交成功'));
}
// 上传文件
public function uploadFormFile()
{
if(!request()->isPost()){
return jsonReturn(-1, lang('请求错误'));
}
$file = request()->file('file');
if(empty($file)){
return jsonReturn(-2,lang('文件不能为空'));
}
$seller_id = $this->sellerId;
// 查看文件类型
$fileName = $file->getOriginalName();
$fileExt = $file->getOriginalExtension();
$file_type = fileFormat($fileName);
$fileType = $this->type[$file_type];
// 附件大小和类型验证
// 获取上传配置
$Settings = new SysSetting();
$uploadSetting = $Settings->getAllCustomArrayData(['parent_id'=>1,'group'=>'upload','status'=>1],'id desc','id,group,title,value')['data'];
$uploadSetting = getColumnForKeyArray($uploadSetting,'title');
$limitSize = $uploadSetting[$file_type.'_size']['value'] * 1024; // byte
$fileSize = $file->getSize(); // 单位byte
if($fileSize > $limitSize){
return jsonReturn(-1,lang('文件过大,请修改上传限制或者替换小的文件'));
}
$extArr = explode(',',$uploadSetting[$file_type.'_ext']['value']);
if(!in_array($fileExt,$extArr)){
return jsonReturn(-2,lang('文件格式错误,请重新上传'));
}
// 文件信息提取
$where = [
'seller_id' => $seller_id,
'group' => 'upload',
'title' => 'storage'
];
$place = new SysSetting();
$type = $place->getSysSetting($where)['data']->toArray()['value'];
$upload = new Upload();
$info = $upload->create($file,$seller_id, $type,$file_type);
if ($info) {
$uploadInfo = $upload->getUploadFileInfo();
if ($uploadInfo['code'] == 0) {
$uploadInfo['data']['type'] = $fileType;
$uploadInfo['data']['size'] = round($fileSize / 1024,2);
$uploadInfo['data']['mime_type'] = $fileExt;
}
return json($uploadInfo);
}else{
return jsonReturn(-1, lang('上传失败请重新尝试'));
}
}
/**
* 下载文件
* @return void
*/
public function downFile()
{
$param = $this->request->param();
if (empty($param)) {
$this->to404();
}
$id = intval($param['id']);
$downFlag = false; // 可下载标识
$downModel = new Down();
$downData = $downModel->where('sub_id', '=', $id)->findOrEmpty();
if (empty($downData)) {
$this->to404();
}
$subContentModel = new SubContent();
$downData['title'] = $subContentModel->where('id', '=', $downData['sub_id'])->value('title');
$formData = [];
if ($downData['is_form'] == '是') {
$formModel = new DiyForm();
$formData = $formModel->where('id', '=', $downData['form_id'])->where('status', '=', 2)->findOrEmpty();
if (empty($formData)) {
$this->to404();
}
$tableName = 'diyform_' . $formData['table'];
$where = [
['sub_id', '=', $id],
['ip', '=', $this->request->ip()],
['user_agent', '=', $this->request->header('user-agent')],
['visitor_id', '=', $this->request->param('visitor_id')]
];
$id = Db::name($tableName)->where($where)->value('id');
if (!empty($id)) {
$downFlag = true;
}
} else {
$downFlag = true;
}
if ($downFlag) {
$fileModel = new Attachment();
$downData['fileArr'] = $fileModel->where('id', 'in', json_decode($downData['file'], true))->select()->toArray();
}
$this->assign('downData', $downData);
$this->assign('formData', $formData);
$this->assign('downFlag', $downFlag);
$this->assign('visitor_id', $this->request->param('visitor_id'));
return $this->fetchSystem('down/index');
}
/**
* 问卷html
* @return string
*/
protected function formHtml()
{
$html = <<<EOF
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{formData_name}</title>
</head>
<body>
<div class="container" style="display: block;max-width:1200px;margin: 0 auto; ">
<div id="app-3" class="formBox" style="padding-top: 50px">
<template>
<form-create
v-model="fapi"
:rule="rule"
:option="option"
@submit="onSubmit"
></form-create>
</template>
</div>
</div>
</body>
<!-- import Vue.js -->
<!-- <script src="//vuejs.org/js/vue.min.js"></script>-->
<!-- 生产环境版本,优化了尺寸和速度 -->
<script src="/system_file/form/vue@2"></script>
<!-- import stylesheet -->
<link rel="stylesheet" href="/system_file/form/index.css">
<!-- import element -->
<script src="/system_file/form/index.js"></script>
<!-- import form-create/element -->
<script src="/system_file/form/form-create.min.js"></script>
<!-- import form-create/designer -->
<script src="/system_file/form/index.min.js"></script>
<script src="/system_file/form/jquery-3.4.1.min.js"></script>
<script>
//FcDesigner 生成的`JSON`
// const FcDesignerRule = '[{"type":"input","field":"cuk5qqdw3umc","title":"输入框","info":"","_fc_drag_tag":"input","hidden":false,"display":true}]';
const FcDesignerRule = '{formData_designContent}';
//FcDesigner 生成的`options`
// const FcDesignerOptions = '{"form":{"labelPosition":"right","size":"mini","labelWidth":"125px","hideRequiredAsterisk":false,"showMessage":true,"inlineMessage":false}}';
const FcDesignerOptions = '{formData_designOption}';
const code = '{formData_code}';
console.log(FcDesignerOptions, 'asdasdasdasdsada')
var app3 = new Vue({
el: '#app-3',
data: {
fapi: null,
rule: formCreate.parseJson(FcDesignerRule),
option: formCreate.parseJson(FcDesignerOptions)
},
methods: {
onSubmit (formData) {
formData.code = code
//todo 提交表单
console.log(formData, '提交表单提交表单提交表单')
$.post('/addFormData.html', formData, function(res) {
console.log(res, '接口返回');
if (res.code == 0) {
alert('提交成功')
} else {
alert('提交失败')
}
});
}
}
})
</script>
</html>
EOF;
return $html;
}
}

View File

@ -0,0 +1,76 @@
<?php
declare (strict_types = 1);
namespace app\controller\frontend;
use app\model\Inquiry;
use app\model\SysSetting;
use app\model\Website;
use app\service\EmailService;
use GuzzleHttp\Client;
use think\facade\Log;
class InquiryController extends BaseController
{
/**
* 保存新建的资源
*
* @return \think\Response
* @throws \app\exception\ModelException
* @throws \app\exception\ModelEmptyException
*/
public function save(): \think\Response
{
$param = request()->param();
if(empty($param)){
return jsonReturn(-1,lang('请填写询盘信息'));
}
// 获取询盘收件箱
$webSite = new Website();
$site = $webSite->getWebsite(['domain' => $this->domain],['inquiryEmail'])['data']->toArray();
$email = [];
if(!empty($site['inquiryEmail'])){
$email = array_column($site['inquiryEmail'],'email');
}
$inquiry = new Inquiry();
$param['seller_id'] = $this->sellerId;
$param['website_id'] = $this->siteId;
$param['ip'] = request()->ip();
$param['inquiry_type'] = 1;
$res = $inquiry -> addInquiry($param);
try{
// 获取suwork配置
$sysSetting = new SysSetting();
$setting = $sysSetting->getAllSysSetting([['title','like','suwork_%']])['data']->toArray();
$key = array_column($setting,'title');
$setting = array_combine($key,$setting);
if(!empty($setting['suwork_appid']['value']) && !empty($setting['suwork_secret']['value']) && !empty($setting['suwork_api']['value'])){
$suworkAccept = config('system.suwork_accept');
$data = array_intersect_key($param,$suworkAccept);
$data['referer_type'] = 1;
$data['referer_web'] = empty($_SERVER['HTTP_REFERER']) ? '未知' : $_SERVER['HTTP_REFERER'];
$data['appid'] = $setting['suwork_appid']['value'];
$data['secret'] = $setting['suwork_secret']['value'];
$data['inquiry_time'] = date('Y-m-d H:i:s',time());
$option = [
'form_params' => $data,
];
$client = new Client();
$response = $client->post($setting['suwork_api']['value'],$option);
$result = json_decode($response->getBody()->getContents(),true);
if($result['code'] != 0){
Log::record($result['msg'],'info');
}
}
// 发送邮件
$EmailService = new EmailService();
$EmailService->sendEmail($this->domain.lang('网站收到询盘'),$email);
}catch (\Exception $e){
Log::error($e->getMessage());
return jsonReturn(0,lang('提交成功'),1);
}
return json($res);
}
}

View File

@ -0,0 +1,55 @@
<?php
declare (strict_types = 1);
namespace app\controller\frontend;
use app\exception\ModelEmptyException;
use app\model\Category;
use app\model\SysSetting;
class ListController extends BaseController
{
/**
* @param Category $Category
* @return mixed
* @throws \app\exception\ModelEmptyException
* @throws \app\exception\ModelException
*/
public function index(Category $Category)
{
$id = (int)input('id');
$with=['thumbnail'=>function($q){
$q->field('id,name,url,type');
},'banner'=>function($q){
$q->field('id,name,url,type');
}
];
try {
$category = $Category->getCategory(['id' => $id, 'seller_id' => $this->sellerId, 'website_id' => $this->siteId,'lang' => $this->lang],$with)['data']->toArray();
}catch (ModelEmptyException $me){
$category = $Category->getCategory(['id' => $id, 'seller_id' => $this->sellerId, 'website_id' => $this->realSiteId,'lang' => $this->lang],$with)['data']->toArray();
}
if (!empty($category['content'])) {
$category['content'] = htmlspecialchars_decode($category['content']);
}
$where = [
'group' => 'company',
'title' => 'huocms_powerby',
];
$huocmsPowerby = SysSetting::where($where)->value('value');
if ($huocmsPowerby) {
$category['seo_title'] .= " - $huocmsPowerby";
$category['seo_keywords'] .= ",$huocmsPowerby";
$category['seo_description'] .= ",$huocmsPowerby";
}
$this->assign('current_cate',$category);
$this->assign('root_domain',$this->rootDomain);
$template = $category['list_tpl'];
return $this->fetch($template);
}
}

View File

@ -0,0 +1,85 @@
<?php
namespace app\controller\frontend;
use app\model\Category;
use app\model\CategorySubContent;
use app\model\SubContent;
use app\model\SysSetting;
class SearchController extends BaseController
{
/**
* @throws \app\exception\ModelEmptyException
* @throws \app\exception\ModelException
* @throws \think\db\exception\DbException
*/
public function index(Category $Category)
{
//拿到搜索页数据主要为了拿seo数据
$with=['thumbnail'=>function($q){
$q->field('id,name,url,type');
},'banner'=>function($q){
$q->field('id,name,url,type');
}
];
$category = $Category->getCategory(['seller_id' => $this->sellerId, 'website_id' => $this->siteId ,'is_search' => 1],$with)['data']->toArray();
$settingWhere = [
'group' => 'company',
'title' => 'huocms_powerby',
];
$huocmsPowerby = SysSetting::where($settingWhere)->value('value');
if ($huocmsPowerby) {
$category['seo_title'] .= " - $huocmsPowerby";
$category['seo_keywords'] .= ",$huocmsPowerby";
$category['seo_description'] .= ",$huocmsPowerby";
}
$this->assign('current_cate',$category);
//搜索
$param = $this->request->param();
$keywords = $param['q'];
$this->assign('keywords',$keywords);
// 获取可以查询的栏目
$category = new Category();
$categories = $category -> getAllCustomArrayData(['is_search' => 1],'id desc','id')['data'];
$cateIds = array_column($categories,'id');
$cateSub = new CategorySubContent();
$cateSubWhere = [
['seller_id','=',$this->siteId],
['category_id','in',$cateIds]
];
$cateSubCon = $cateSub->getAllCategorySubContent($cateSubWhere)['data'];
$subIds = array_column($cateSubCon,'sub_content_id');
$SubContent = new SubContent();
$content = $SubContent->with(['thumbnail'=>function($q){
$q->field('id,name,url,type');
},'category'])->where(['seller_id'=>$this->sellerId,'is_del'=>1]);
if(!empty($param['q'])){
$content = $content -> whereLike('title|description|sub_title','%' . $keywords . '%');
}
$param['siteId'] = $this->siteId;
$param['sellerId'] = $this->sellerId;
$data = $content->whereIn('id',$subIds)
->order('id','desc')
->paginate([
'page' => $param['page'] ?? 1,
'list_rows' => $param['limit'] ?? 10,
])->each(function (&$item)use($param){
if(!empty($item['category'])) {
$item['category'] = $item->toArray()['category'];
$item['category_id'] = $item['category'][0]['id'];
}
$item['thumbnail'] = $item->toArray()['thumbnail'];
});
$data->appends(['q' => $keywords]);
$this->assign('list', $data->items());
$this->assign('list_page', $data->render());
return $this->fetch(config('view.view_search_page_name'));
}
}

View File

@ -0,0 +1,55 @@
<?php
declare (strict_types = 1);
namespace app\controller\frontend;
class SeoController extends BaseController
{
/**
* robots文件读取
*
*/
public function getRobots()
{
$filename = $this->siteId . '_robots.txt';
$path = app() -> getRootPath() . 'public/robots/' . $filename ;
if(!hcFileExist($path)){
$sysRobots = app() -> getRootPath() . 'public/robots/robots.txt' ;
touch($path);
copy($sysRobots,$path);
}
header("Content-type:text/html;");
$content = file_get_contents($path);
$content = '<pre>' . str_replace("\n",'<pre>',$content);
echo $content;
exit;
}
/**
* sitemap xml文件读取
*
*/
public function getSitemapXml()
{
$filename = $this->siteId . '_sitemap.xml';
$path = app() -> getRootPath() . 'public/xml/' . $filename;
$content = @file_get_contents($path);
header("Content-type:text/xml;");
echo $content;
exit;
}
/**
* sitemap html文件读取
*
*/
public function getSitemapHtml()
{
$filename = $this->siteId . '_sitemap.html';
$path = app() -> getRootPath() . 'public/sitemap_html/' . $filename;
echo @file_get_contents($path);
exit;
}
}

View File

@ -0,0 +1,59 @@
<?php
declare (strict_types = 1);
namespace app\controller\frontend;
use app\model\Tag;
use think\Request;
class TagController extends BaseController
{
/**
* 显示资源列表
*
* @return \think\Response
*/
public function index()
{
$tagModel = new Tag();
//标签整合展示
$param = $this->request->param();
$allTag = $tagModel->where([
'website_id' => $this->siteId,
'seller_id' => $this->sellerId,
'lang' => $this->lang,
])->group('first_letter')->column('first_letter');
$charArr = ['A', 'B', 'C', 'D', 'E', 'F', 'G', "H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"];
foreach ($charArr as $key => $value) {
if (!in_array($value, $allTag)) {
unset($charArr[$key]);
}
}
$charArr = array_values($charArr);
$this->assign('allTag', $charArr);
$param['name'] = $param['name'] ?? $charArr[0];
$param['page'] = $param['page'] ?? 1;
$param['limit'] = $param['limit'] ?? 80;
//默认获取A首字母开头的标签
$tagList = $tagModel->where([
'website_id' => $this->siteId,
'seller_id' => $this->sellerId,
'lang' => $this->lang,
])->where('first_letter', '=', $param['name'])->paginate(
[
'page' => $param['page'],
'list_rows' => $param['limit'],
]
);
$this->assign('tagList', $tagList);
$this->assign('name', $param['name']);
return $this->fetch(config('view.view_tag_page_name'));
}
}

19
app/event.php Normal file
View File

@ -0,0 +1,19 @@
<?php
// 事件定义文件
return [
'bind' => [
],
'listen' => [
'AppInit' => [],
'HttpRun' => [],
'HttpEnd' => [],
'LogLevel' => [],
'LogWrite' => [],
'AdminLoginLog' => [\app\listener\AdminLoginListener::class],
'AdminOptLog' => [\app\listener\AdminOperationListener::class],
],
'subscribe' => [
],
];

View File

@ -0,0 +1,23 @@
<?php
namespace app\exception;
class AuthException extends BaseException
{
protected $errCode = 403;
protected $errMsg = '抱歉,您没有权限,请联系管理处理';
public function __construct($msg = null,$code = null)
{
if(!$msg){
$msg = $this->errMsg;
}
if(!$code){
$code = $this->errCode;
}
parent::__construct($msg,$code);
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace app\exception;
class BadSysSettingException extends BaseException
{
protected $errCode = 500025;
protected $errMsg = '配置未完成';
public function __construct($msg = null,$code = null)
{
if(!$msg){
$msg = $this->errMsg;
}
if(!$code){
$code = $this->errCode;
}
parent::__construct($msg,$code);
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace app\exception;
use think\Exception;
class BaseException extends Exception
{
protected $errCode = 5000;
protected $errMsg = '系统错误';
public function __construct($msg = null, $code = null)
{
if(!$msg){
$msg = $this->errMsg;
}
if(!$code){
$code = $this->errCode;
}
parent::__construct($msg,$code);
}
}

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