commit 31e9432ba08a08f68b47dd8d934195199c99d79f
Author: wangxinglong <2371974647@qq.com>
Date: Tue Feb 22 17:27:27 2022 +0800
初始化
diff --git a/.env.bak b/.env.bak
new file mode 100644
index 0000000..d35b5f6
--- /dev/null
+++ b/.env.bak
@@ -0,0 +1,19 @@
+APP_DEBUG = true
+APP_TRACE = true
+
+[APP]
+DEFAULT_TIMEZONE = Asia/Shanghai
+
+[DATABASE]
+TYPE = mysql
+HOSTNAME = 211.149.245.223
+DATABASE = dev_bee_cms
+USERNAME = dev_bee_cms
+PASSWORD = dT7yH5fmd28JG6ER
+HOSTPORT = 3306
+CHARSET = utf8mb4
+DEBUG = true
+PREFIX = bee_
+
+[LANG]
+default_lang = zh-cn
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..79e5639
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,14 @@
+.env
+/.idea
+/.vscode
+backup/data/*
+/config/extra
+*.log
+runtime/*
+storage/*
+public/storage/*
+.DS_Store
+/Test.php
+nginx.htaccess
+dump.rdb
+/app/controller/api/Test.php
\ No newline at end of file
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..574a39c
--- /dev/null
+++ b/LICENSE.txt
@@ -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.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..156dc98
--- /dev/null
+++ b/README.md
@@ -0,0 +1,61 @@
+## 本CMS基于ThinkPHP 6.0开发
+
+> - 运行环境要求 PHP7.4+
+> - MySql版本限制 5.7+
+
+
+
+### 功能列表 ###
+
+> - [x] 后台菜单管理
+> - [x] 后台权限管理
+> - [x] 管理员管理
+> - [x] 操作日志
+> - [x] 配置文件配置
+> - [x] 素材管理
+> - [x] 内容管理
+> - [x] 栏目管理
+> - [x] 模型管理
+> - [x] 配置管理
+
+
+
+### 待办功能列表 ###
+
+> - 迁移|重构老版CMS功能
+>> 碎片管理【block高度自定义配置】
+> - 系统
+> - 配置管理重构
+ >> 需求 配置优先查询配置文件,文件不存在则根据数据库生成配置文件
+> - 内容管理重构
+ >> 拆分内容表为基础表,根据模型建表创建额外表
+> - 模型管理重构
+> - 相关SEO配置
+> - 组图插件完善
+>> 可排序、可自定义关联字段 如:标题、url、alt、描述、等等关联可自由增减
+
+
+
+
+### PHP扩展依赖列表 ###
+
+> - fileinfo
+> - exif 需要安装在mbsting扩展之后
+> - gd
+
+
+
+### PHP组件列表 ###
+
+> - [图片处理 Intervention Image](http://image.intervention.io/getting_started/introduction)
+> - [权限验证 think-authz](https://github.com/php-casbin/think-authz)
+
+
+
+### 前端组件列表 ###
+
+> - [xm-Select 下拉选择框](https://gitee.com/maplemei/xm-select)
+> - [富文本编辑器选择 wangEditorv3.1.1](https://github.com/wangfupeng1988/wangEditor)
+
+
+
diff --git a/app/.htaccess b/app/.htaccess
new file mode 100644
index 0000000..3418e55
--- /dev/null
+++ b/app/.htaccess
@@ -0,0 +1 @@
+deny from all
\ No newline at end of file
diff --git a/app/common.php b/app/common.php
new file mode 100644
index 0000000..8ae718d
--- /dev/null
+++ b/app/common.php
@@ -0,0 +1,666 @@
+
+// +----------------------------------------------------------------------
+
+use think\Collection;
+use think\db\exception\DbException;
+use think\exception\ClassNotFoundException;
+use think\Model;
+use think\Paginator;
+
+// 应用公共文件
+
+if (!function_exists('widget')) {
+ /**
+ * 渲染输出Widget
+ * @param string $name Widget名称
+ * @param array $data 传入的参数
+ * @return mixed
+ * milo 2019-05-08 从TP5.1代码中拿来修改的
+ */
+ function widget($name, $data = [])
+ {
+ return action($name, $data, 'widget');
+ }
+}
+if (!function_exists('action')) {
+ /**
+ * 调用模块的操作方法 参数格式 [模块/控制器/]操作
+ * @param string $url 调用地址
+ * @param string|array $vars 调用参数 支持字符串和数组
+ * @param string $layer 要调用的控制层名称
+ * @param bool $appendSuffix 是否添加类名后缀
+ * @return mixed
+ * milo 2019-05-08 从TP5.1代码中拿来修改的
+ */
+ function action($url, $vars = [], $layer = 'controller', $appendSuffix = false)
+ {
+ $info = pathinfo($url);
+ $action = $info['basename'];
+ $module = '.' != $info['dirname'] ? $info['dirname'] : request()->controller();
+ $class = controller($module, $layer);
+ if (is_scalar($vars)) {
+ if (strpos($vars, '=')) {
+ parse_str($vars, $vars);
+ } else {
+ $vars = [$vars];
+ }
+ }
+ return app()->invokeMethod([$class, $action . config('route.action_suffix')], $vars);
+ }
+}
+if (!function_exists('controller')) {
+ /**
+ * 实例化(分层)控制器 格式:[模块名/]控制器名
+ * @access public
+ * @param string $name 资源地址
+ * @param string $layer 控制层名称
+ * @param bool $appendSuffix 是否添加类名后缀
+ * @param string $empty 空控制器名称
+ * @return object
+ * @throws ClassNotFoundException
+ *
+ * milo 2019-05-08 从TP5.1代码中拿来修改的
+ */
+ function controller($name, $layer = 'controller', $empty = '')
+ {
+ $class = parseClass($name, $layer);
+ if (class_exists($class)) {
+ return app()->make($class);
+ } elseif ($empty && class_exists($emptyClass = app()->parseClass($layer, $empty))) {
+ return app()->make($emptyClass);
+ }
+
+ throw new ClassNotFoundException('class not exists:' . $class, $class);
+ }
+}
+
+if (!function_exists('parseClass')) {
+ /**
+ * 解析模块和类名
+ * @access protected
+ * @param string $name 资源地址
+ * @param string $layer 验证层名称
+ * @param bool $appendSuffix 是否添加类名后缀
+ * @return array
+ *
+ * milo 2019-05-08 从TP5.1代码中拿来修改的
+ */
+ function parseClass($name, $layer)
+ {
+ if (false !== strpos($name, '\\')) {
+ $class = $name;
+ } else {
+ if (strpos($name, '/')) {
+ $names = explode('/', $name, 2);
+ $name = $names[1];
+ }
+ $class = app()->parseClass($layer, $name);
+ }
+ return $class;
+ }
+}
+
+
+if (!function_exists('randomStr')) {
+ /**
+ * 获取随机字符串
+ * @param int $type 0:数字(默认);1:全部;2:小写字母;3:大写字母;4:字母;
+ * @param int $len 字符串长度
+ * @return string
+ */
+ function randomStr($type = 0, $len = 5)
+ {
+ $strPol = "0123456789";
+ if ($type == 1) {
+ $strPol = "ABCDEFGHIJKLMOPQRSTUVWYZ0123456789abcdefghijklmopqrstuvwyz";
+ } elseif ($type == 2) {
+ $strPol = "abcdefghijklmopqrstuvwyz";
+ } elseif ($type == 3) {
+ $strPol = "ABCDEFGHIJKLMOPQRSTUVWYZ";
+ } elseif ($type == 4) {
+ $strPol = "ABCDEFGHIJKLMOPQRSTUVWYZabcdefghijklmopqrstuvwyz";
+ }
+ $max = strlen($strPol) - 1;
+ $str = '';
+ for ($i = 0; $i < $len; $i++) {
+ $str .= $strPol[rand(0, $max)];
+ }
+ return $str;
+ }
+}
+
+if (!function_exists('isMobile')) {
+ //判断访问终端是否为移动端
+ function isMobile()
+ {
+ // 如果有HTTP_X_WAP_PROFILE则一定是移动设备
+ if (isset($_SERVER['HTTP_X_WAP_PROFILE'])) {
+ return true;
+ }
+ // 如果via信息含有wap则一定是移动设备,部分服务商会屏蔽该信息
+ if (isset($_SERVER['HTTP_VIA'])) {
+ // 找不到为flase,否则为true
+ return stristr($_SERVER['HTTP_VIA'], "wap") ? true : false;
+ }
+ // 脑残法,判断手机发送的客户端标志,兼容性有待提高。其中'MicroMessenger'是电脑微信
+ if (isset($_SERVER['HTTP_USER_AGENT'])) {
+ $clientkeywords = [
+ 'nokia', 'sony', 'ericsson', 'mot', 'samsung', 'htc', 'sgh', 'lg', 'sharp', 'sie-', 'philips',
+ 'panasonic', 'alcatel', 'lenovo', 'iphone', 'ipod', 'blackberry', 'meizu', 'android', 'netfront',
+ 'symbian', 'ucweb', 'windowsce', 'palm', 'operamini', 'operamobi', 'openwave', 'nexusone', 'cldc',
+ 'midp', 'wap', 'mobile', 'MicroMessenger'
+ ];
+ // 从HTTP_USER_AGENT中查找手机浏览器的关键字
+ if (preg_match("/(" . implode('|', $clientkeywords) . ")/i", strtolower($_SERVER['HTTP_USER_AGENT']))) {
+ return true;
+ }
+ }
+ // 协议法,因为有可能不准确,放到最后判断
+ if (isset ($_SERVER['HTTP_ACCEPT'])) {
+ // 如果只支持wml并且不支持html那一定是移动设备
+ // 如果支持wml和html但是wml在html之前则是移动设备
+ if ((strpos($_SERVER['HTTP_ACCEPT'], 'vnd.wap.wml') !== false) && (strpos($_SERVER['HTTP_ACCEPT'],
+ 'text/html') === false || (strpos($_SERVER['HTTP_ACCEPT'],
+ 'vnd.wap.wml') < strpos($_SERVER['HTTP_ACCEPT'], 'text/html')))) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
+//根据栏目获取路径
+if (!function_exists('getUri')) {
+ function getUri($category)
+ {
+ if (!empty($category) && isset($category['id'])) {
+ if (empty($category['route'])) {
+ return '/page/' . $category['id'] . ".html";
+ } else {
+ return $category['route'];
+ }
+ }
+ return '';
+ }
+}
+
+//根据文件大小转换为文字
+if (!function_exists('sizeToStr')) {
+ function sizeToStr($size)
+ {
+ if (!is_numeric($size) || $size <= 0) {
+ return '';
+ }
+ $size = $size / 1024;
+ if ($size < 1024) {
+ return sprintf("%.2fK", $size);
+ }
+ $size = $size / 1024;
+ if ($size < 1024) {
+ return sprintf("%.2fM", $size);
+ }
+ $size = $size / 1024;
+ return sprintf("%.2fG", $size);
+ }
+}
+
+//根据路径获取文件名
+if (!function_exists('getKeyByPath')) {
+ function getKeyByPath($path)
+ {
+ return substr($path, strrpos($path, '/') + 1);
+ }
+}
+
+//富文本中提取图片路径
+if (!function_exists('getImageUrlFromText')) {
+ function getImageUrlFromText($content)
+ {
+ preg_match_all('//is', $content, $imgs);
+ $data = [];
+ if (!empty($imgs) && !empty($imgs[1])) {
+ foreach ($imgs[1] as $img) {
+ if (substr($img, 0, 4) != 'http') {
+ $key = getKeyByPath($img);
+ $data[$key] = $img;
+ }
+ }
+ }
+ return $data;
+ }
+}
+
+//富文本中提取视频路径
+if (!function_exists('getVideoUrlFromText')) {
+ function getVideoUrlFromText($content)
+ {
+ preg_match_all('//is', $content, $videos);
+ $data = [];
+ if (!empty($videos) && !empty($videos[1])) {
+ foreach ($videos[1] as $video) {
+ if (substr($video, 0, 4) != 'http') {
+ $key = getKeyByPath($video);
+ $data[$key] = $video;
+ }
+ }
+ }
+ return $data;
+ }
+}
+
+//获取目录下的所有文件
+if (!function_exists('getAllFilesByPath')) {
+ function getAllFilesByPath($path, $rootPath)
+ {
+ if (is_dir($path) && file_exists($path)) {
+ $items = scandir($path);
+ $files = [];
+ foreach ($items as $item) {
+ if (substr($item, 0, 1) != '.' && strpos($item, '_') == false) {
+ $itemPath = $path . '/' . $item;
+ if (is_file($itemPath)) {
+ $size = filesize($itemPath);
+ $files[$item] = [
+ 'path' => str_replace($rootPath, '/', $itemPath),
+ 'realPath' => $itemPath,
+ 'size' => $size,
+ 'sizeStr' => sizeToStr($size),
+ 'suffix' => strtolower(substr($item, strrpos($item, '.') + 1)) //后缀
+ ];
+ } elseif (is_dir($itemPath)) {
+ $childFiles = getAllFilesByPath($itemPath, $rootPath);
+ if (!empty($childFiles)) {
+ $files = array_merge($files, $childFiles);
+ } else {
+ rmdir($itemPath);
+ }
+ }
+ }
+ }
+ return $files;
+ }
+ return [];
+ }
+}
+
+//过滤get输入
+if (!function_exists('getFilter')) {
+ function getFilter($value)
+ {
+ $getFilter = "'|(and|or)\b.+?(>|<|=|in|like)|\/\*.+?\*\/|<\s*script\b|\bEXEC\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\s+(TABLE|DATABASE)";
+ $forArray = false;
+ if (is_array($value)) {
+ $forFilter = implode($value);
+ $forArray = true;
+ } else {
+ $forFilter = $value;
+ }
+ if (preg_match("/" . $getFilter . "/is", $forFilter) == 1) {
+ $value = $forArray ? [] : '';
+ }
+ return filterExp($value);
+ }
+}
+
+//过滤post录入
+if (!function_exists('postFilter')) {
+ function postFilter($value)
+ {
+ $postFilter = "\b(and|or)\b.{1,6}?(=|>|<|\bin\b|\blike\b)|\/\*.+?\*\/|<\s*script\b|\bEXEC\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\s+(TABLE|DATABASE)";
+ $forArray = false;
+ if (is_array($value)) {
+ $forFilter = implode($value);
+ $forArray = true;
+ } else {
+ $forFilter = $value;
+ }
+ if (preg_match("/" . $postFilter . "/is", $forFilter) == 1) {
+ $value = $forArray ? [] : '';
+ }
+ return filterExp($value);
+ }
+}
+
+//过滤cookie数据
+if (!function_exists('cookieFilter')) {
+ function cookieFilter($value)
+ {
+ $cookieFilter = "\b(and|or)\b.{1,6}?(=|>|<|\bin\b|\blike\b)|\/\*.+?\*\/|<\s*script\b|\bEXEC\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\s+(TABLE|DATABASE)";
+ $forArray = false;
+ if (is_array($value)) {
+ $forFilter = implode($value);
+ $forArray = true;
+ } else {
+ $forFilter = $value;
+ }
+ if (preg_match("/" . $cookieFilter . "/is", $forFilter) == 1) {
+ $value = $forArray ? [] : '';
+ }
+ return filterExp($value);
+ }
+}
+
+if (!function_exists('filterExp')) {
+ function filterExp($value)
+ {
+ $filter = '/^(\s)+(EXP|NEQ|GT|EGT|LT|ELT|OR|XOR|LIKE|NOTLIKE|NOT LIKE|NOT BETWEEN|NOTBETWEEN|BETWEEN|NOTIN|NOT IN|IN)(\s)+$/i';
+ $forArray = false;
+ if (is_array($value)) {
+ $forFilter = implode($value);
+ $forArray = true;
+ } else {
+ $forFilter = $value;
+ }
+ if (preg_match($filter, $forFilter) == 1) {
+ $value = $forArray ? [] : '';
+ }
+ return $value;
+ }
+}
+
+if (!function_exists('arrayHtmlFilter')) {
+ function arrayHtmlFilter($arr)
+ {
+ foreach ($arr as $k => $one) {
+ if (is_array($one)) {
+ $arr[$k] = arrayHtmlFilter($one);
+ } else {
+ $one = trim($one);
+ $arr[$k] = strip_tags($one);
+ }
+ }
+ return $arr;
+ }
+}
+
+if (!function_exists('toCamelString')) {
+ /**
+ * 转驼峰
+ *
+ * @param string $string
+ * @param false $small 默认false 为true首字母小写
+ * @return string
+ */
+ function toCamelString(string $string, bool $small = false): string
+ {
+ //例: xxx_yYy_zzZ 和 xxX-yyy-Zzz
+ //1. 字符串转小写 xxx_yyy_zzz xxx-yyy-zzz
+ //2. -和_转空格 xxx yyy zzz
+ //3. 单词首字母大写 Xxx Yyy Zzz
+ //4. 清除空格 XxxYyyZzz
+ //处理下划线、减号 统统专程大驼峰 如xxx_yyy_zzz xxx-yyy-zzz 均转为 XxxYyyZzz
+ $string = strtolower($string);
+ $string = str_replace('-', ' ', $string);
+ $string = str_replace('_', ' ', $string);
+ $string = str_replace(' ', '', ucwords($string));
+ if ($small) {
+ $string = lcfirst($string);
+ }
+ return $string;
+ }
+}
+
+if (!function_exists('unCamelize')) {
+ /**
+ * 驼峰命名转特定分隔符[如下划线]命名
+ * @param string $camelCaps
+ * @param string $separator
+ * @return string
+ */
+ function unCamelize(string $camelCaps, string $separator = '-'): string
+ {
+ return strtolower(preg_replace('/([a-z])([A-Z])/', "$1" . $separator . "$2", $camelCaps));
+ }
+}
+
+
+if (!function_exists('generateRand')) {
+ /**
+ * 生成随机数
+ *
+ * @param int $length 长度
+ * @param string $type 模式 默认mix混合 number纯数字 alpha字母
+ * @return string
+ */
+ function generateRand(int $length = 8, string $type = 'mix'): string
+ {
+ $alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
+ $number = '0123456789';
+ $alphabetLen = strlen($alphabet) - 1;
+ $numberLen = 9;
+
+ switch ($type) {
+ case 'number':
+ $str = $number;
+ $len = $numberLen;
+ break;
+ case 'alpha':
+ $str = $alphabet;
+ $len = $alphabetLen;
+ break;
+ default:
+ $str = $alphabet . $number;
+ $len = $alphabetLen + $numberLen;
+ }
+
+ $randStr = '';
+ $str = str_shuffle($str);
+ for ($i = 0; $i < $length; $i++) {
+ $num = mt_rand(0, $len);
+ $randStr .= $str[$num];
+ }
+ return $randStr;
+ }
+}
+
+if (!function_exists('generateCode')) {
+ /**
+ * 生成特定编码
+ *
+ * @param string $type 类型 默认mix混合 number纯数字 alpha字母
+ * @param int $len 随机数长度
+ * @return string
+ */
+ function generateCode(string $type = 'number', int $len = 6): string
+ {
+ //#时间戳+微秒+6位随机数
+ $time = microtime(true);
+ $timeStr = str_replace('.', '', $time);
+ return sprintf("%s%s", $timeStr, generateRand($len, $type));
+ }
+}
+
+if (!function_exists('checkMobile')) {
+ /**
+ * 检测手机号
+ *
+ * @param string $mobile
+ * @return bool
+ */
+ function checkMobile(string $mobile): bool
+ {
+ if (preg_match("/^1[3456789]{1}\d{9}$/", $mobile)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
+
+if (!function_exists('arrayKeysFilter')) {
+ /**
+ * 数组键名过滤
+ *
+ * @param array $data
+ * @param array $allowKeys
+ * @return array
+ */
+ function arrayKeysFilter(array $data, array $allowKeys): array
+ {
+ $list = [];
+ foreach ($data as $key => $val) {
+ if (in_array($key, $allowKeys)) {
+ $list[$key] = $val;
+ }
+ }
+ return $list;
+ }
+}
+
+if (!function_exists('getFilesize')) {
+ /**
+ * 尺寸单位转换
+ *
+ * @param $num
+ * @return string
+ */
+ function getFilesize($num): string
+ {
+ $p = 0;
+ $format = 'B';
+ if ($num > 0 && $num < 1024) {
+ return number_format($num) . ' ' . $format;
+ }
+ if ($num >= 1024 && $num < pow(1024, 2)) {
+ $p = 1;
+ $format = 'KB';
+ }
+ if ($num >= pow(1024, 2) && $num < pow(1024, 3)) {
+ $p = 2;
+ $format = 'MB';
+ }
+ if ($num >= pow(1024, 3) && $num < pow(1024, 4)) {
+ $p = 3;
+ $format = 'GB';
+ }
+ if ($num >= pow(1024, 4) && $num < pow(1024, 5)) {
+ $p = 3;
+ $format = 'TB';
+ }
+ $num /= pow(1024, $p);
+ return number_format($num, 3) . ' ' . $format;
+ }
+}
+
+if (!function_exists('arrayNullToString')) {
+ /**
+ * 数组|或数据集中null值转为空字符串,并以数组格式返回
+ * 通常用于api json 返回内容null转换
+ *
+ * @param array $data 【array|collection】
+ * @return array
+ */
+ function arrayNullToString($data)
+ {
+ if ($data instanceof Collection || $data instanceof Model) {
+ $data = $data->toArray();
+ }
+ // 判断是否可以遍历
+ if (is_iterable($data)) {
+ foreach ($data as $key => $val) {
+ if ($val instanceof Collection || $data instanceof Model) {
+ $val = $val->toArray();
+ }
+ if (is_iterable($val)) {
+ $data[$key] = arrayNullToString($val);
+ } elseif ($val === null) {
+ $data[$key] = '';
+ }
+ }
+ } else {
+ $data = [];
+ }
+ return $data;
+ }
+}
+
+if (!function_exists('arrayKeysExcludeFilter')) {
+ /**
+ * 数组键名排除过滤
+ *
+ * @param array $data
+ * @param array $excludeKeys 排除的字段
+ * @return array
+ */
+ function arrayKeysExcludeFilter(array $data, array $excludeKeys): array
+ {
+ foreach ($data as $key => $val) {
+ if (in_array($key, $excludeKeys)) {
+ unset($data[$key]);
+ }
+ }
+ return $data;
+ }
+}
+
+if (!function_exists('getLatelyWeekDate')) {
+ /**
+ * 获取本周的周一 到周日的日期
+ */
+ function getLatelyWeekDate()
+ {
+ //本周一
+ $oneDate = date('Y-m-d 00:00:00', (time() - ((date('w') == 0 ? 7 : date('w')) - 1) * 86400)); //w为星期几的数字形式,这里0为周日
+ $oneDateTime = strtotime($oneDate);
+ //返回周一到周天 1-7
+ return [
+ "1" => ["date"=>$oneDate],
+ "2" => ["date"=>date('Y-m-d 00:00:00', $oneDateTime + ((86400 * 1)))],
+ "3" => ["date"=>date('Y-m-d 00:00:00', $oneDateTime + ((86400 * 2)))],
+ "4" => ["date"=>date('Y-m-d 00:00:00', $oneDateTime + ((86400 * 3)))],
+ "5" => ["date"=>date('Y-m-d 00:00:00', $oneDateTime + ((86400 * 4)))],
+ "6" => ["date"=>date('Y-m-d 00:00:00', $oneDateTime + ((86400 * 5)))],
+ "7" => ["date"=>date('Y-m-d 00:00:00', $oneDateTime + ((86400 * 6)))],
+ ];
+ }
+}
+
+if (!function_exists('stringDesensitization')) {
+ /**
+ * 字符串脱敏 默认给手机号脱敏 其他未兼容
+ *
+ * @param string $string
+ * @param string $s
+ * @param int $start
+ * @param int $len
+ * @return string
+ */
+ function stringDesensitization(string $string, string $s = '****', int $start = 3, int $len = 4): string
+ {
+ return substr_replace($string, $s, $start, $len);
+ }
+}
+
+if (!function_exists('checkPathExistWithMake')) {
+ /**
+ * 检测文件夹是否存在,不存在时自动创建(需要有写入权限)
+ * 支持递归创建
+ *
+ * @param string $absolutePath
+ * @return bool
+ */
+ function checkPathExistWithMake(string $absolutePath): bool
+ {
+ try {
+ $absolutePath = rtrim($absolutePath, '/');
+ if (empty($absolutePath)) {
+ return false;
+ }
+
+ if (!is_dir($absolutePath)) {
+ return mkdir($absolutePath, 0777, true);
+ }
+
+ return true;
+ } catch (\Exception $e) {
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/controller/Base.php b/app/controller/Base.php
new file mode 100644
index 0000000..40597e3
--- /dev/null
+++ b/app/controller/Base.php
@@ -0,0 +1,85 @@
+ 0,//当前选中的顶级栏目
+ "links"=> [],//友情连接
+ "article"=> [],//文章
+ "blocks"=> [],//碎片
+ ];
+ //系统配置信息
+ protected $system = [];
+
+ protected $auth = [];
+
+ protected $authId = 0;
+
+ protected $aboutCategory = [];
+
+ // 初始化
+ protected function initialize()
+ {
+ $this->auth = session('frontend_auth') ?? [];
+ $this->data['auth'] = $this->auth;
+ $this->authId = $this->auth['id'] ?? 0;
+ //加载基础配置
+ Config::load('extra/base', 'extra_system');
+ $this->system = config("extra_system");
+ $this->data['system'] = $this->system;
+ $this->setSeo();
+ $this->setLinks();
+ $this->setActiveCategory(ArchivesCategoryModel::index_id);
+ }
+
+ //设置选中的栏目
+ protected function setActiveCategory($id)
+ {
+ $this->data["active_category_id"] = $id;
+ }
+
+ //设置友情链接 缓存1小时
+ protected function setLinks()
+ {
+ if(Cache::has("links")){
+ $this->data["links"] = Cache::get("links",[]);
+ }else{
+ $links = Link::findList([],[],1,20,null,["sort"=>"asc"]);
+ if(isset( $links['list'])){
+ $this->data["links"] = $links['list']->toArray();
+ }else{
+ $this->data["links"] = [];
+ }
+ Cache::set("links",$this->data["links"],3600);
+ }
+ }
+
+ //设置SEO信息
+ protected function setSeo($title = '', $keywords = '', $description = '')
+ {
+ $this->data['seoTitle'] = $title ?: $this->system['seo_title'] ?? '';
+ $this->data['seoKeywords'] = $keywords ?: $this->system['seo_keywords'] ?? '';
+ $this->data['seoDescription'] = $description ?: $this->system['seo_description'] ?? '';
+ }
+
+ //模板
+ protected function view($template = '')
+ {
+ return view($template)->assign($this->data);
+ }
+}
diff --git a/app/controller/BaseController.php b/app/controller/BaseController.php
new file mode 100644
index 0000000..35482fa
--- /dev/null
+++ b/app/controller/BaseController.php
@@ -0,0 +1,254 @@
+app = $app;
+ $this->request = $this->app->request;
+
+ // 控制器初始化
+ $this->initialize();
+ }
+
+ // 初始化
+ protected function initialize()
+ {
+ }
+
+ /**
+ * 验证数据
+ * @access protected
+ * @param array $data 数据
+ * @param string|array $validate 验证器名或者验证规则数组
+ * @param array $message 提示信息
+ * @param bool $batch 是否批量验证
+ * @return array|string|true
+ * @throws ValidateException
+ */
+ protected function validate(array $data, $validate, array $message = [], bool $batch = false)
+ {
+ if (is_array($validate)) {
+ $v = new Validate();
+ $v->rule($validate);
+ } else {
+ if (strpos($validate, '.')) {
+ // 支持场景
+ list($validate, $scene) = explode('.', $validate);
+ }
+ $class = false !== strpos($validate, '\\') ? $validate : $this->app->parseClass('validate', $validate);
+ $v = new $class();
+ if (!empty($scene)) {
+ $v->scene($scene);
+ }
+ }
+
+ $v->message($message);
+
+ // 是否批量验证
+ if ($batch || $this->batchValidate) {
+ $v->batch(true);
+ }
+
+ return $v->failException(true)->check($data);
+ }
+
+ /**
+ * 验证器
+ *
+ * @param array $data
+ * @param $validate
+ * @param array $message
+ * @param bool $batch
+ * @return Json|bool
+ * @throws Exception
+ */
+ protected function validateByApi(array $data, $validate, array $message = [], bool $batch = false)
+ {
+ try {
+ $this->validate($data, $validate, $message, $batch);
+ return true;
+ } catch (ValidateException $e) {
+ $msg = $e->getMessage();
+ if ($batch) {
+ $msg = implode(',', $e->getError());
+ }
+
+ return $this->json(4000, $msg);
+ } catch (Exception $e) {
+ throw $e;
+ }
+ }
+
+ /**
+ * 验证器
+ *
+ * @param array $data
+ * @param $validate
+ * @param array $message
+ * @param bool $batch
+ * @return Redirect
+ * @throws Exception
+ */
+ protected function validateByView(array $data, $validate, array $message = [], bool $batch = false): Redirect
+ {
+ try {
+ $this->validate($data, $validate, $message, $batch);
+ } catch (ValidateException $e) {
+ $msg = $e->getMessage();
+ if ($batch) {
+ $msg = implode(',', $e->getError());
+ }
+
+ return $this->error( $msg);
+ } catch (Exception $e) {
+ throw $e;
+ }
+ }
+
+ /**
+ * 操作成功跳转的快捷方法
+ * @access protected
+ * @param mixed $msg 提示信息
+ * @param string|null $url 跳转的URL地址
+ * @param mixed $data 返回的数据
+ * @param integer $wait 跳转等待时间
+ * @param array $header 发送的Header信息
+ * @return Redirect
+ */
+ protected function success($msg = '', string $url = null, $data = '', int $wait = 3, array $header = []): Redirect
+ {
+ if (is_null($url) && isset($_SERVER["HTTP_REFERER"])) {
+ $url = $_SERVER["HTTP_REFERER"];
+ } elseif ($url) {
+ $url = (strpos($url, '://') || 0 === strpos($url, '/')) ? $url : $this->app->route->buildUrl($url);
+ }
+
+ $result = [
+ 'code' => 1,
+ 'msg' => $msg,
+ 'data' => $data,
+ 'url' => $url,
+ 'wait' => $wait,
+ ];
+ return $this->redirect(url('error/404', $result));
+ }
+
+ /**
+ * 操作错误跳转的快捷方法
+ * @access protected
+ * @param mixed $msg 提示信息
+ * @param string|null $url 跳转的URL地址
+ * @param mixed $data 返回的数据
+ * @param integer $wait 跳转等待时间
+ * @return Redirect
+ */
+ protected function error($msg = '', string $url = null, $data = '', int $wait = 3): Redirect
+ {
+ if (is_null($url)) {
+ $referer = $_SERVER['HTTP_REFERER'] ?? null;
+ if (empty($referer)) {
+ $url = $this->request->isAjax() ? '' : '/';
+ } else {
+ $url = $this->request->isAjax() ? '' : 'javascript:history.back(-1);';
+ }
+ } elseif ($url) {
+ $url = (strpos($url, '://') || 0 === strpos($url, '/')) ? $url : $this->app->route->buildUrl($url);
+ }
+ $result = [
+ 'code' => 0,
+ 'msg' => $msg,
+ 'data' => $data,
+ 'url' => $url,
+ 'wait' => $wait,
+ ];
+
+ return $this->redirect(url('error/404', $result));
+ }
+
+ /**
+ * 返回封装后的API数据到客户端
+ * 以json格式抛出异常
+ * @access protected
+ * @param integer $code 返回的code
+ * @param mixed $msg 提示信息
+ * @param mixed $data 要返回的数据
+ * @return Json
+ */
+ protected function json(int $code = 0, $msg = '操作成功', $data = []): Json
+ {
+ $result = [
+ 'code' => $code,
+ 'msg' => $msg,
+ 'data' => $data
+ ];
+ return json($result);
+ }
+
+ /**
+ * URL重定向
+ * @access protected
+ * @param string $url 跳转的URL表达式
+ * @return Redirect
+ */
+ protected function redirect($url): Redirect
+ {
+ if (!is_string($url)) {
+ $url = $url->__toString();
+ }
+ return redirect($url);
+ }
+}
\ No newline at end of file
diff --git a/app/controller/Error.php b/app/controller/Error.php
new file mode 100644
index 0000000..972f2c5
--- /dev/null
+++ b/app/controller/Error.php
@@ -0,0 +1,48 @@
+isAjax()) {
+ return $this->json(4004, 'error request!');
+ } else {
+ $referer = $_SERVER['HTTP_REFERER'] ?? null;
+ if (empty($referer)) {
+ $url = '/';
+ } else {
+ $domain = $this->request->domain();
+ $urlInfo = parse_url($referer);
+ $scheme = $urlInfo['scheme'] ?? '';
+ $requestSrc = '';
+ if (!empty($scheme)) {
+ $requestSrc = $scheme.'://'.($urlInfo['host'] ?? '');
+ }
+ if($domain != $requestSrc) {
+ $url = '/';
+ } else {
+ $url = 'javascript:history.back(-1);';
+ }
+ }
+ $result = [
+ 'code' => 404,
+ 'msg' => '无效请求! 没有找到相关资源',
+ 'data' => [],
+ 'url' => $url,
+ 'wait' => 5,
+ ];
+ return view('/manager/error/jump')->assign($result);
+ }
+ }
+
+
+ public function jump()
+ {
+ $param = request()->param();
+ return view()->assign($param);
+ }
+}
\ No newline at end of file
diff --git a/app/controller/Feedback.php b/app/controller/Feedback.php
new file mode 100644
index 0000000..1f46982
--- /dev/null
+++ b/app/controller/Feedback.php
@@ -0,0 +1,75 @@
+request->isPost()) {
+ return $this->json(4001, '请求方式错误!');
+ }
+
+ $params = [
+ 'user_name' => $this->request->post('user_name/s', ''),
+ 'user_tel' => $this->request->post('user_tel/s', ''),
+ 'user_email' => $this->request->post('user_email/s', ''),
+ 'content' => $this->request->post('content/s', ''),
+ 'code' => $this->request->post('code/s', ''),
+ ];
+
+ try{
+ $validate = $this->validateByApi($params, [
+ 'code|验证码'=>'require|captcha',
+ 'user_name|姓名' => 'chs|min:2|max:30',
+ 'user_tel|联系电话' => 'min:5|max:20|mobile',
+ 'user_email|邮箱地址' => 'email',
+ 'content|留言内容' => 'require|min:6|max:500',
+ ]);
+ if ($validate !== true) {
+ return $validate;
+ }
+
+ if ($this->authId > 0) {
+ $account = AccountRepository::getInstance()->findById($this->authId);
+ if ($account) {
+ $params['user_name'] = $params['user_name'] ?: ($account->nickname ?? '');
+ $params['user_tel'] = $params['user_tel'] ?: ($account->mobile ?? '');
+ }
+ }
+
+ FeedbackModel::create([
+ 'account_id' => $this->authId,
+ 'user_name' => $params['user_name'] ?: '',
+ 'user_tel' => $params['user_tel'] ?: '',
+ 'user_email' => $params['user_email'],
+ 'content' => $params['content'],
+ 'created_at' => date('Y-m-d H:i:s'),
+ ]);
+
+
+ return $this->json(0, '感谢你的留言!');
+ } catch (\Exception $e) {
+ return $this->json(5001, '服务器异常,留言信息提交失败!');
+ }
+
+ }
+
+ public function code()
+ {
+ return Captcha::create('verify');
+ }
+}
\ No newline at end of file
diff --git a/app/controller/Index.php b/app/controller/Index.php
new file mode 100644
index 0000000..c321167
--- /dev/null
+++ b/app/controller/Index.php
@@ -0,0 +1,39 @@
+data['slide'] = Slide::findList([["position","=",SlidePosition::home_position]])['list'];
+ $this->data['topCategoryId'] = ArchivesCategory::index_id ;
+
+ //碎片
+ $this->data['blocks'] = Block:: getByCategoryId(ArchivesCategory::index_id);
+
+ return $this->view();
+ }catch (RepositoryException $e){
+ return $this->error("服务器错误");
+ }
+
+
+ }
+}
\ No newline at end of file
diff --git a/app/controller/Page.php b/app/controller/Page.php
new file mode 100644
index 0000000..3e98c42
--- /dev/null
+++ b/app/controller/Page.php
@@ -0,0 +1,150 @@
+redirect("/");
+ }
+ $category = ArchivesCategoryModel::findById($categoryId);
+ if(empty($category)){
+ return $this->error("内容不存在");
+ }
+
+
+ //如果有链接
+ if(!empty($category['link'])){
+ return $this->redirect($category['link']);
+ }
+
+ $this->data["category"] = $category;
+ $this->setActiveCategory($category['id']);
+ $this->data['topCategoryId'] = ArchivesCategoryModel::firstGradeById($category['id']) ;
+
+ $categoryModel = ArchivesModel::allModel();
+ //所有类型都要把碎片加上
+ $this->data["blocks"] = Block:: getByCategoryId($category['id']);
+
+ switch ($category['model_id']){
+ //文章模型
+ case $categoryModel[ArchivesModel::MODEL_ARCHIVES]:
+ return $this->archives($category,$category['cover_template']);
+ break;
+ default:
+ return $this->redirect("/");
+ }
+
+ }
+
+ //文章列表
+
+ /**
+ *
+ * @param $categoryId 栏目id
+ * @param $categoryTemplate 模板
+ */
+ protected function archives($category,$categoryTemplate)
+ {
+ //动态设置当前分页驱动
+ app('think\App')->bind(Paginator::class, DxtcPage::class);
+ $this->data["archives"] = Archives::getListPageByCategory($category['id'],$category['page_size']);
+ return $this->view(empty($categoryTemplate)?"archives_default":$categoryTemplate);
+ }
+
+
+ /**
+ *
+ * @param $categoryId 栏目id
+ * @param $categoryTemplate 模板
+ */
+ protected function page($categoryTemplate)
+ {
+ return $this->view(empty($categoryTemplate)?"page_default":$categoryTemplate);
+ }
+
+ /**
+ * 文章详情
+ *
+ * @param $articleId
+ */
+ public function archivesInfo()
+ {
+ $articleId = input("articleId/d",0);
+ $archive = Archives::findOne([["id","=",$articleId]]);
+ if(empty($archive)){
+ return $this->error("内容不存在");
+ }
+ $archive->inc("views")->update();
+ $this->data["archive"] = $archive;
+
+
+ $category = ArchivesCategoryModel::findById($archive['category_id']);
+ if(empty($category)){
+ return $this->error("内容不存在");
+ }
+ $this->data["category"] = $category;
+ $this->setActiveCategory($category['id']);
+ $this->data['topCategoryId'] = ArchivesCategoryModel::firstGradeById($category['id']) ;
+
+
+ //所有类型都要把碎片加上
+ $this->data["blocks"] = Block:: getByCategoryId($category['id']);
+
+
+ $seo_title = empty($archive['seo_title'])
+ ?
+ $archive['title']
+ :
+ $archive['seo_title'];
+ $seo_keywords = empty($archive['seo_keywords'])
+ ?
+ ""
+ :
+ $archive['seo_keywords'];
+ $seo_description = empty($archive['seo_description'])
+ ?
+ ""
+ :
+ $archive['seo_description'];
+
+
+ $this->setSeo( $seo_title,$seo_keywords,$seo_description);
+
+
+ // 上一篇
+ $this->data["prev"] = Archives::findOne([["category_id","=",$category['id']], ['sort', '<', $archive['sort']]]
+ , [], function ($q) {
+ return $q->with(["archivesCategory"])->order(['sort'=> 'desc']);
+ });
+
+ // 下一篇
+ $this->data["next"] = Archives::findOne([["category_id","=",$category['id']], ['sort', '>', $archive['sort']]]
+ , [], function ($q) {
+ return $q->with(["archivesCategory"])->order(['sort'=> 'asc']);
+ });
+
+ return $this->view(!empty($category['detail_template'])?$category['detail_template']:"archives_default");
+ }
+
+
+}
\ No newline at end of file
diff --git a/app/controller/api/Archives.php b/app/controller/api/Archives.php
new file mode 100644
index 0000000..46347a0
--- /dev/null
+++ b/app/controller/api/Archives.php
@@ -0,0 +1,20 @@
+middleware = [
+ 'jwt',
+ 'apiLogin' => ['except' => $this->noNeedLogin]
+ ];
+ }
+
+ public function __call($method, $args)
+ {
+ return $this->json(4004, 'error request!');
+ }
+}
\ No newline at end of file
diff --git a/app/controller/api/Common.php b/app/controller/api/Common.php
new file mode 100644
index 0000000..d97a3b9
--- /dev/null
+++ b/app/controller/api/Common.php
@@ -0,0 +1,82 @@
+scene('send_sms')->check($input)) {
+ return $this->json(4001, '参数错误');
+ }
+ if (!in_array($input['type'], ['register', 'login', 'binding'])) {
+ return $this->json(4002, '参数错误');
+ }
+
+ CommonRepository::getInstance()->sendSms($input['phone'], $input['type']);
+ return $this->json();
+ }
+
+ /**
+ * 查看轮播图可视位置配置信息
+ */
+ public function slidePositions(): Json
+ {
+ $repo = OperationRepository::getInstance();
+ $list = $repo->slidePositions();
+ return $this->json(0, 'success', $list);
+ }
+
+
+ /**
+ * 轮播图
+ */
+ public function slides(): Json
+ {
+ $size = $this->request->param('size/d', 0);
+ $position = trim($this->request->param('position/s', ''));
+ if (empty($position)) {
+ return $this->json();
+ }
+
+ try {
+ $repo = OperationRepository::getInstance();
+ $list = $repo->slideListByPosition($position, $size);
+ } catch (\Exception $e) {
+ $list = new Collection();
+ }
+
+ return $this->json(0, 'success', $list);
+ }
+
+
+}
\ No newline at end of file
diff --git a/app/controller/api/Index.php b/app/controller/api/Index.php
new file mode 100644
index 0000000..8dbd961
--- /dev/null
+++ b/app/controller/api/Index.php
@@ -0,0 +1,41 @@
+ 0, 'msg' => 'I am index']);
+ }
+
+ /**
+ * 测试用
+ *
+ * @return Json
+ * @throws RepositoryException
+ */
+ public function test(): Json
+ {
+ $userId = $this->request->middleware('userInfo')['user_id'] ?? 0;
+ $user = AccountRepository::getInstance()->info($userId, []);
+ return json(['code' => 0, 'msg' => 'I am test ', 'data' => $user]);
+ }
+
+
+
+
+}
\ No newline at end of file
diff --git a/app/controller/api/User.php b/app/controller/api/User.php
new file mode 100644
index 0000000..4089826
--- /dev/null
+++ b/app/controller/api/User.php
@@ -0,0 +1,15 @@
+ 0, 'msg' => 'I am index']);
+ }
+
+
+}
\ No newline at end of file
diff --git a/app/controller/api/file/Upload.php b/app/controller/api/file/Upload.php
new file mode 100644
index 0000000..afe4f94
--- /dev/null
+++ b/app/controller/api/file/Upload.php
@@ -0,0 +1,132 @@
+isCompress = $system['compress'] ?? true;
+ }
+ $this->validate = new VUpload();
+ $this->uploadPath = Config::get('filesystem.disks.local.url');
+ if(is_writable(app()->getRootPath() . 'public' . $this->uploadPath)){
+ $this->uploadPathIsWritable = true;
+ }
+
+ $this->cancelTimeLimit();
+ }
+
+
+ /**
+ * 通用文件上传
+ * @return Json
+ */
+ public function file()
+ {
+ $file = request()->file('file');
+ if (empty($file)) {
+ return $this->json(4001, '请上传的文件');
+ }
+
+ if($this->validate->checkFile($file)){
+ try{
+ if(!$this->uploadPathIsWritable){
+ throw new \Exception('上传文件夹需要写入权限');
+ }
+ $src = Filesystem::putFile('files/'.date('Ym'), $file, 'uniqid');
+ $src = $this->uploadPath . '/' . $src;
+ $return['src'] = $src;
+ $return['name'] = $file->getOriginalName();
+
+ //加入上传文件表
+ File::add($file, $src, $file->md5());
+ } catch (\Exception $e) {
+ return $this->json(4003, $e->getMessage());
+ }
+
+ return $this->json(0,'success', $return);
+ }else{
+
+ $errorMsg = Lang::get($this->validate->getError());
+ return $this->json(4002, $errorMsg);
+ }
+ }
+
+ /**
+ * 通用图片上传
+ * @return Json
+ */
+ public function image()
+ {
+ $image = request()->file('image');
+ if (empty($image)) {
+ return $this->json(4001, '请上传图片文件');
+ }
+ $md5 = $image->md5();//文件md5
+ if($this->validate->checkImage($image)){
+ try{
+ if(!$this->uploadPathIsWritable){
+ throw new \Exception('上传文件夹需要写入权限');
+ }
+ $src = Filesystem::putFile('images/'.date('Ym'), $image, 'uniqid');
+ $src = $this->uploadPath . '/' . $src;
+ $return['src'] = $src;
+ if($this->isCompress){
+ Image::resize($src);
+ }
+
+ //加入上传文件表
+ File::add($image, $src,$md5);
+ } catch (\Exception $e) {
+ return $this->json(4003, $e->getMessage());
+ }
+
+ return $this->json(0, 'success', $return);
+ }else{
+
+ $errorMsg = Lang::get($this->validate->getError());
+ return $this->json(4002, $errorMsg);
+ }
+ }
+
+
+ /**
+ * 同步到OOS服务器存储
+ * @param string $src
+ */
+ private function syncToOos(string $src)
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/app/controller/manager/Account.php b/app/controller/manager/Account.php
new file mode 100644
index 0000000..d36af98
--- /dev/null
+++ b/app/controller/manager/Account.php
@@ -0,0 +1,736 @@
+findById($id, [], function ($q) {
+ return $q->with(['serviceList']);
+ });
+
+ $statusList = [
+ Order::STATUS_SHIPPED, Order::STATUS_PAID, Order::STATUS_COMPLETED
+ ];
+ $consumption = OrderRepository::getInstance()->userOrderList($id, [], 1, 0, $statusList);
+ $orderNum = 0;
+ $orderScoreNum = 0;
+ $totalPrice = 0;
+ $totalScore = 0;
+ $totalCoin = 0;
+ $consumption->each(function ($item) use (&$totalPrice, &$totalScore, &$totalCoin, &$orderScoreNum, &$orderNum) {
+ if ($item->is_score == AccountModel::COMMON_ON) {
+ $orderScoreNum += 1;
+ } else {
+ $orderNum += 1;
+ }
+ $totalPrice += $item->price;
+ $totalScore += $item->score;
+ $totalCoin += $item->coin;
+ });
+ $item['total_price'] = Math::fen2Yuan($totalPrice);
+ $item['total_score'] = $totalScore;
+ $item['total_coin'] = $totalCoin;
+ $item['order_num'] = $orderNum;
+ $item['order_score_num'] = $orderScoreNum;
+ $item['order_newest'] = $consumption->toArray()[0] ?? [];
+ $item['customer_service'] = $item->serviceList->name ?? '';
+ $item['source_text'] = AccountRepository::getInstance()->getSourceDetail($id);
+ $item['channel_text'] = AccountModel::channelTextList()[$item['channel']] ?? '';
+
+ $this->data['item'] = $item;
+
+ return $this->view();
+ }
+
+ /**
+ * 编辑
+ *
+ * @return Json|View
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ * @throws Exception
+ */
+ public function edit()
+ {
+ $id = input('id/d', 0);
+ if (!$info = AccountRepository::getInstance()->findById($id)) {
+ if ($this->request->isPost()) {
+ return $this->json(4000, '用户不存在');
+ } else {
+ return $this->error('用户不存在');
+ }
+ }
+
+ if ($this->request->isPost()) {
+ $item = input('post.');
+ $validate = $this->validateByApi($item, [
+ 'nickname' => 'require',
+ ]);
+
+ if ($validate !== true) {
+ return $validate;
+ }
+
+ try {
+ $info->save($item);
+ return $this->json();
+ } catch (ValidateException $e) {
+ return $this->json(4001, $e->getError());
+ }
+ }
+
+ $this->data['item'] = $info;
+
+ return $this->view();
+ }
+
+ /**
+ * 单个字段编辑
+ *
+ * @return Json
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ * @throws Exception
+ */
+ public function modify(): Json
+ {
+ if ($this->request->isPost()) {
+ $item = input('post.');
+ $validate = $this->validateByApi($item, [
+ 'field' => 'require',
+ 'value' => 'require',
+ ]);
+
+ if ($validate !== true) {
+ return $validate;
+ }
+
+ if (!$info = AccountModel::findById($item['id'])) {
+ return $this->json(4001, '记录不存在');
+ }
+
+ $update = [$item['field'] => $item['value']];
+
+ try {
+ $info->save($update);
+ return $this->json();
+ } catch (ValidateException $e) {
+ return $this->json(4001, $e->getError());
+ }
+ }
+ return $this->json(4000, '非法请求');
+ }
+
+ /**
+ * 列表
+ *
+ * @return View|Json
+ * @throws Exception
+ */
+ public function index()
+ {
+ if ($this->request->isPost()) {
+ $page = input('page/d', 1);
+ $size = input('size/d', 20);
+ $searchParams = input('searchParams');
+ $search = [];
+ $other = [];
+ if ($searchParams) {
+ foreach ($searchParams as $key => $param) {
+ if ($key == 'tag' && !empty($param)) {
+ $other['tag_id'] = $param;
+ continue;
+ }
+ if ($param || $param == '0') {
+ $search[] = [$key, 'like', '%'.$param.'%'];
+ }
+ }
+ }
+
+ $search[] = ['phone_active', '=', AccountModel::COMMON_ON];
+
+ // 后台绑定的账号
+ $accountId = $this->auth['account_id'] ?? 0;
+
+ try {
+ $items = AccountRepository::getInstance()->customerList($search, [], $accountId, $page, $size, function ($q) use ($other) {
+ return $q->when(isset($other['tag_id']), function ($query) use ($other) {
+ $query->leftJoin('account_tag_pivot atp', 'atp.account_id = id')->where('atp.tag_id', $other['tag_id']);
+ });
+ });
+ return $this->json(0, '操作成功', $items);
+ } catch (RepositoryException $e) {
+ return $this->json(4001, $e->getMessage());
+ } catch (Exception $e) {
+ return $this->json(5001, '获取用户列表失败'.$e->getMessage());
+ }
+ }
+
+ $this->data['channelList'] = AccountModel::channelTextList();
+ $this->data['customerList'] = Staff::getCustomerServiceList();
+ $this->data['tagList'] = AccountTag::getTags();
+
+ return $this->view();
+ }
+
+ /**
+ * 分配员工
+ *
+ * @return View|Json
+ * @throws Exception
+ */
+ public function staff()
+ {
+ $id = input('id/s', '');
+ if ($this->request->isPost()) {
+ $ids = input('ids/s');
+ $staffId = input('staff_id/d', 0);
+ if (empty($ids)) {
+ return $this->json(4001, '请选择要操作的用户');
+ }
+
+ if (!$staffId) {
+ return $this->json(4001, '请选择分配的员工');
+ }
+
+ $ids = explode(',', $ids);
+
+ try {
+ CustomerReceive::allotServiceByBatch($ids, $staffId);
+ return $this->json(0, '操作成功');
+ } catch (RepositoryException $e) {
+ return $this->json(4001, $e->getMessage());
+ } catch (Exception $e) {
+ Log::error('分配指定员工失败'.$e->getMessage());
+ return $this->json(5001, '分配指定员工失败');
+ }
+ }
+
+ $staffList = Staff::getCustomerServiceList();
+ // 分配的是客服分组下所有客服
+ $this->data['servicerList'] = json_encode($staffList, JSON_UNESCAPED_UNICODE);
+ $this->data['id'] = $id;
+
+ return $this->view();
+ }
+
+ /**
+ * 分配客户标签
+ *
+ * @return View|Json
+ * @throws Exception
+ */
+ public function tag()
+ {
+ $id = input('id/s', '');
+ if ($this->request->isPost()) {
+ $ids = input('ids/s');
+ $tagId = input('tag_id/s');
+ if (empty($ids)) {
+ return $this->json(4001, '请选择要操作的用户');
+ }
+
+ if (empty($tagId)) {
+ return $this->json(4001, '请选择分配的标签');
+ }
+
+ $ids = explode(',', $ids);
+ $tags = explode(',', $tagId);
+
+ Db::startTrans();
+ try {
+ // 删除所有人标签
+ AccountTagPivot::whereIn('account_id', $ids)->delete();
+ // 新增标签
+ $insert = [];
+ foreach ($ids as $id) {
+ foreach ($tags as $tag) {
+ $arr = [];
+ $arr['account_id'] = $id;
+ $arr['tag_id'] = $tag;
+
+ $insert[] = $arr;
+ }
+ }
+
+ (new AccountTagPivot())->saveAll($insert);
+ Db::commit();
+ return $this->json(0, '操作成功');
+ } catch (RepositoryException $e) {
+ Db::rollback();
+ return $this->json(4001, $e->getMessage());
+ } catch (Exception $e) {
+ Db::rollback();
+ Log::error('分配客户标签失败'.$e->getMessage());
+ return $this->json(5001, '分配客户标签失败');
+ }
+ }
+
+ $tagList = AccountTag::order('sort', 'desc')->order('id', 'asc')->select()->toArray();
+
+ // 分配的是线上客服
+ $this->data['tagList'] = json_encode($tagList, JSON_UNESCAPED_UNICODE);
+ $this->data['id'] = $id;
+
+ return $this->view();
+ }
+
+ /**
+ * 分配客户来源
+ *
+ * @return View|Json
+ * @throws Exception
+ */
+ public function source()
+ {
+ $id = input('id/s', '');
+ if ($this->request->isPost()) {
+ $ids = input('ids/s');
+ $channel = input('channel/s');
+ if (empty($ids)) {
+ return $this->json(4001, '请选择要操作的用户');
+ }
+
+ if (empty($channel)) {
+ return $this->json(4001, '请选择分配的客户来源');
+ }
+
+ $value = $channel == AccountModel::CHANNEL_MEMBER ? input('type_staff') : input('type_'.$channel);
+ $ids = explode(',', $ids);
+
+ Db::startTrans();
+ try {
+ $field = 'id,channel,inviter_account_id,inviter_parent_id,source_code';
+ $accountList = AccountModel::whereIn('id', $ids)->column($field, 'id');
+ $update = [];//更新account表
+ $insert = [];//插入account_operate_log表
+
+ switch ($channel) {
+ case AccountModel::CHANNEL_NORMAL:
+ // 设为自然流量 清空上级邀请人和上上级邀请人
+ $update = ['inviter_account_id' => 0, 'inviter_parent_id' => 0, 'channel' => $channel, 'source_code' => null];
+ break;
+ case AccountModel::CHANNEL_CUSTOMER:
+ case AccountModel::CHANNEL_MEMBER:
+ // 客户分享或员工分享 修改上级邀请人ID
+ $update = ['inviter_account_id' => $value, 'channel' => $channel, 'source_code' => null];
+ break;
+ case AccountModel::CHANNEL_ACTIVITY:
+ // 活码分享
+ $update = ['inviter_account_id' => 0, 'source_code' => $value, 'channel' => $channel];
+ break;
+ }
+
+ $now = date('Y-m-d H:i:s');
+ $createdBy = $this->auth['user_id'] ?? 0;
+ foreach ($ids as $id) {
+ $log = [];
+ $originalSource = $accountList[$id] ?? [];
+ switch ($channel) {
+ case AccountModel::CHANNEL_NORMAL:
+ $log['description'] = sprintf("从原始来源【channel=%s,inviter_account_id=%s,source_code=%s】变更为新来源【channel=%s,inviter_account_id=%s,source_code=%s】",
+ $originalSource['channel'] ?? '', $originalSource['inviter_account_id'] ?? 0,
+ $originalSource['source_code'] ?? '', $channel, 0, null);
+ break;
+ case AccountModel::CHANNEL_CUSTOMER:
+ case AccountModel::CHANNEL_MEMBER:
+ $log['description'] = sprintf("从原始来源【channel=%s,inviter_account_id=%s,source_code=%s】变更为新来源【channel=%s,inviter_account_id=%s,source_code=%s】",
+ $originalSource['channel'] ?? '', $originalSource['inviter_account_id'] ?? 0,
+ $originalSource['source_code'] ?? '', $channel, $value, null);
+ break;
+ case AccountModel::CHANNEL_ACTIVITY:
+ $log['description'] = sprintf("从原始来源【channel=%s,inviter_account_id=%s,source_code=%s】变更为新来源【channel=%s,inviter_account_id=%s,source_code=%s】",
+ $originalSource['channel'] ?? '', $originalSource['inviter_account_id'] ?? 0,
+ $originalSource['source_code'] ?? '', $channel, 0, $value);
+ break;
+ }
+
+ $log['type'] = AccountOperateLog::TYPE_CHANGE_SOURCE;
+ $log['account_id'] = $id;
+ $log['created_at'] = $now;
+ $log['created_by'] = $createdBy;
+ $insert[] = $log;
+ }
+
+ (new AccountOperateLog())->saveAll($insert);
+ (new AccountModel())->whereIn('id', $ids)->save($update);
+ Db::commit();
+ return $this->json(0, '操作成功');
+ } catch (RepositoryException $e) {
+ Db::rollback();
+ return $this->json(4001, $e->getMessage());
+ } catch (Exception $e) {
+ Db::rollback();
+ Log::error('分配客户标签失败'.$e->getMessage());
+ return $this->json(5001, '分配客户标签失败');
+ }
+ }
+
+ // 客服来源
+ $this->data['channelList'] = AccountModel::channelTextList();
+ $this->data['id'] = $id;
+
+ return $this->view();
+ }
+
+ /**
+ * 到店设置
+ *
+ * @return View|Json
+ * @throws Exception
+ */
+ public function sign()
+ {
+ $id = input('id/s', '');
+ if ($this->request->isPost()) {
+ $ids = input('ids/s');
+ $sign = input('sign/d');
+ if (empty($ids)) {
+ return $this->json(4001, '请选择要操作的用户');
+ }
+
+ if (!in_array($sign, [AccountModel::COMMON_ON, AccountModel::COMMON_OFF])) {
+
+ return $this->json(4001, '请选择是否到店');
+ }
+
+ $ids = explode(',', $ids);
+
+ Db::startTrans();
+ try {
+ (new AccountModel())->whereIn('id', $ids)->save(['is_sign' => $sign]);
+ Db::commit();
+ return $this->json(0, '操作成功');
+ } catch (Exception $e) {
+ Db::rollback();
+ Log::error('是否到店操作失败'.$e->getMessage());
+ return $this->json(5001, '是否到店操作失败');
+ }
+ }
+
+ $this->data['id'] = $id;
+
+ return $this->view();
+ }
+
+ /**
+ * 获取员工列表
+ *
+ * @return Json
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException|Exception
+ */
+ public function getStaffList(): Json
+ {
+ if ($this->request->isPost()) {
+ $keyword = input('keyword/s', '');
+ $type = input('type/s', '');//员工类型标识 如在线客服=customer-online
+ $page = input('page/d', 1);
+ $size = input('size/d', 0);
+ $id = input('id/d', 0);
+
+ $relationIds = [];//已选记录
+ if ($id > 0 && $activity = Activity::findById($id)) {
+ $relationIds = explode(',', $activity['account_id']);
+ }
+
+ $res = Staff::getStaff($type, $keyword, $page, $size);
+
+ if ($res['total'] > 0) {
+ $res['list'] = $res['list']->toArray();
+ foreach ($res['list'] as &$item) {
+ if (count($relationIds) > 0 && in_array($item['id'], $relationIds)) {
+ $item['selected'] = true;
+ }
+ }
+ }
+
+ return $this->json(0, '操作成功', $res);
+ }
+ return $this->json(4001, '非法请求');
+ }
+
+ /**
+ * 获取客户列表
+ *
+ * @return Json
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException|Exception
+ */
+ public function getAccountList(): Json
+ {
+ if ($this->request->isPost()) {
+ $keyword = input('keyword/s', '');
+ $page = input('page/d', 1);
+ $size = input('size/d', 10);
+ $id = input('id', '');
+
+ $relationIds = explode(',', $id);//已选记录
+
+ $where = [];
+ $where[] = ['is_staff', '=', AccountModel::COMMON_OFF];
+ if (!empty($keyword)) {
+ $where[] = ['nickname|real_name|mobile', 'like', '%'.$keyword.'%'];
+ }
+
+ $res = AccountModel::findList($where, ['id', 'nickname', 'real_name', 'mobile'], $page, $size);
+
+ if ($res['total'] > 0 && $relationIds) {
+ $res['list'] = $res['list']->toArray();
+ foreach ($res['list'] as &$item) {
+ $item['name_text'] = sprintf("昵称:%s;真实姓名:%s,手机号:%s", $item['nickname'], $item['real_name'], $item['mobile']);
+ if (count($relationIds) > 0 && in_array($item['id'], $relationIds)) {
+ $item['selected'] = true;
+ }
+ }
+ }
+
+ return $this->json(0, '操作成功', $res);
+ }
+ return $this->json(4001, '非法请求');
+ }
+
+ /**
+ * 充值孔雀币
+ * */
+ public function rechargeCoin()
+ {
+ $id = input('id/s');
+ if ($this->request->isPost()) {
+ $ids = input('ids/s');
+ if (empty($ids)) {
+ return $this->json("4003", "请选择用户");
+ }
+ $ids = explode(",", $ids);
+ if (count($ids) > 1000) {
+ return $this->json("4003", "一次最多选择1000条");
+ }
+
+ $coin = input("coin/d", 1, "abs");
+
+ $Account = AccountRepository::getInstance()->getModel()
+ ->where("id", "in", $ids)->lock(true)
+ ->select();
+
+ Db::startTrans();
+ try {
+ AccountRepository::getInstance()->getModel()->where("id", "in", $ids)->inc("coin", $coin)->update();
+ $time = date("Y-m-d H:i:s");
+ $dataLog = [];
+ $dataOperationLog = [];
+ $Account->each(function ($item) use ($coin, $time, &$dataLog, &$dataOperationLog) {
+ $dataLog[] = [
+ "account_id" => $item["id"],
+ "operator" => ($this->auth['nickname'] ?? ""),
+ "operator_id" => $this->auth['user_id'] ?? 0,
+ "name" => "后台充值孔雀币",
+ "num" => $coin,
+ "type" => AccountDataLog::TYPE_COIN,
+ "action" => AccountDataLog::ACTION_ADMIN_RECHARGE,
+ "created_at" => $time,
+ "surplus" => ($item["coin"] + $coin),
+ ];
+ $dataOperationLog[] = [
+ "account_id" => $item["id"],
+ "operator" => ($this->auth['nickname'] ?? ""),
+ "num" => $coin,
+ "remark" => "后台充值孔雀币",
+ "type" => AccountDataLog::TYPE_COIN,
+ "created_at" => $time,
+ ];
+ });
+
+ AccountDataLog::insertAll($dataLog);
+ AccountDataOperationLog::insertAll($dataOperationLog);
+
+ Db::commit();
+ return $this->json();
+ } catch (Exception $e) {
+ Db::rollback();
+ return $this->json("5003", "充值失败-1:".$e->getMessage());
+ } catch (RepositoryException $e) {
+ Db::rollback();
+ return $this->json("5003", "充值失败-2:".$e->getMessage());
+ }
+
+ }
+ $this->data['id'] = $id;
+
+ return $this->view();
+ }
+
+ /**
+ * 用户等级管理
+ * */
+ public function accountLevel()
+ {
+ if ($this->request->isPost()) {
+ $page = input('page/d', 1);
+ $limit = input('size/d', 10);
+ $items = AccountLevel::findList([], [], $page, $limit, null, ["value" => "desc"]);
+
+ $items["list"]->each(function (&$item) {
+ if (empty($item["rights"])) {
+ return;
+ }
+ $str = "";
+ foreach (explode(",", $item["rights"]) as $ritem) {
+ foreach (AccountLevel::$rightsArray as $mitem) {
+ if ($mitem["key"] == $ritem) {
+ $str .= $mitem["title"]." ";
+ }
+ }
+ }
+ $item->rights = $str;
+ });
+
+ return $this->json(0, '操作成功', $items);
+ }
+ return $this->view();
+ }
+
+ /**
+ * 添加用户等级管理
+ * */
+ public function addAccountLevel()
+ {
+ if ($this->request->isPost()) {
+ $item = input('post.');
+ $rule = [
+ 'name|名称' => 'require',
+ 'value|成长值' => 'require',
+ ];
+ $validate = $this->validateByApi($item, $rule);
+ if ($validate !== true) {
+ return $validate;
+ }
+ try {
+ AccountLevel::create($item);
+ return $this->json();
+ } catch (ValidateException $e) {
+ return $this->json(4001, $e->getError());
+ }
+ }
+
+ //权益列表
+ $rightsArray = AccountLevel::$rightsArray;
+ $this->data['rightsJson'] = json_encode($rightsArray);
+
+ return $this->view();
+ }
+
+ /**
+ * 添加用户等级管理
+ * */
+ public function delAccountLevel()
+ {
+ if ($this->request->isPost()) {
+ $id = input('id/d');
+ $item = AccountLevel::findById($id);
+ if (empty($item)) {
+ return $this->json(4001, "信息不存在");
+ }
+ AccountLevel::destroy($id);
+ return $this->json();
+ }
+
+ }
+
+ /**
+ * 编辑用户等级管理
+ * */
+ public function editAccountLevel()
+ {
+ $id = input("id/d");
+ $info = AccountLevel::findById($id);
+ if ($this->request->isPost()) {
+ if (empty($info)) {
+ return $this->json(4001, "信息不存在");
+ }
+ $item = $this->request->only(["name", "rights", "poster", "value", "content"]);
+ $rule = [
+ 'name|名称' => 'require',
+ 'value|成长值' => 'require',
+ ];
+ $validate = $this->validateByApi($item, $rule);
+ if ($validate !== true) {
+ return $validate;
+ }
+ try {
+ AccountLevel::updateById($id, $item);
+ return $this->json();
+ } catch (ValidateException $e) {
+ return $this->json(4001, $e->getError());
+ }
+ }
+
+ if (empty($info)) {
+ return $this->error("信息不存在");
+ }
+
+
+ $this->data['item'] = $info;
+
+ //权益列表
+ $rightsArray = AccountLevel::$rightsArray;
+ $selectd = [];
+ if (!empty($info['rights'])) {
+ $selectd = explode(",", $info['rights']);
+ }
+ $this->data['rightsJson'] = AccountLevel::xmSelectJson($selectd);
+
+ return $this->view();
+ }
+
+
+}
\ No newline at end of file
diff --git a/app/controller/manager/Archives.php b/app/controller/manager/Archives.php
new file mode 100644
index 0000000..d9d0dd0
--- /dev/null
+++ b/app/controller/manager/Archives.php
@@ -0,0 +1,306 @@
+request->isPost()) {
+ $ids = input('post.ids/a', []);
+ if (empty($ids)) {
+ $ids[] = input('post.id/d');
+ }
+ ArchivesModel::deleteByIds($ids);
+ return $this->json();
+ }
+ return $this->json(4001, '非法请求!');
+ }
+
+ /**
+ * 编辑
+ *
+ * @return Json|View
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ * @throws Exception
+ */
+ public function edit()
+ {
+ $id = input('id/d', 0);
+
+ if (!$info = ArchivesModel::findById($id)) {
+ return $this->json(4001, '记录不存在');
+ }
+
+ if ($this->request->isPost()) {
+ $item = input('post.');
+ if (isset($item['video_src'])) {
+ $item['video'] = $item['video_src'];
+ unset($item['video_src']);
+ }
+
+ $validate = $this->validateByApi($item, [
+ 'category_id|栏目' => 'require|gt:0',
+ 'title|标题' => 'require|max:255',
+ 'summary|摘要' => 'max:255',
+ 'content|内容' => 'require',
+ ], ['category_id.gt' => '请选择栏目']);
+
+ if ($validate !== true) {
+ return $validate;
+ }
+
+ try {
+ $now = date('Y-m-d H:i:s');
+ $item['updated_at'] = $now;
+ $item['updated_by'] = $this->auth['user_id'];
+ $info->save($item);
+ return $this->json();
+ } catch (ValidateException $e) {
+ return $this->json(4001, $e->getError());
+ }
+ }
+
+ $showFieldList = ArchivesModelField::showFieldList();//所有栏目 可展示字段列表
+ $currentShowFields = $showFieldList[$info['category_id']] ?? [];//当前选中栏目 可展示字段列表
+
+ $this->data['item'] = $info;
+ $this->data['jsonList'] = $this->xmSelectJson([$info['category_id']]);
+ $this->data['currentList'] = $currentShowFields;
+
+ return $this->view();
+ }
+
+ /**
+ * 单个字段编辑
+ *
+ * @return Json
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ * @throws Exception
+ */
+ public function modify(): Json
+ {
+ if ($this->request->isPost()) {
+ $item = input('post.');
+ $validate = $this->validateByApi($item, [
+ 'field' => 'require',
+ 'value' => 'require',
+ ]);
+
+ if ($validate !== true) {
+ return $validate;
+ }
+
+ if (!$info = ArchivesModel::findById($item['id'])) {
+ return $this->json(4001, '记录不存在');
+ }
+
+ $update = [$item['field'] => $item['value']];
+
+ try {
+ $info->save($update);
+ return $this->json();
+ } catch (ValidateException $e) {
+ return $this->json(4001, $e->getError());
+ }
+ }
+ return $this->json(4000, '非法请求');
+ }
+
+ /**
+ * 添加
+ *
+ * @return Json|View
+ * @throws Exception
+ */
+ public function add()
+ {
+ $categoryId = input('category_id/d', 0);
+
+ if ($this->request->isPost()) {
+ $item = input('post.');
+ if (isset($item['video_src'])) {
+ $item['video'] = $item['video_src'];
+ unset($item['video_src']);
+ }
+
+ $validate = $this->validateByApi($item, [
+ 'category_id|栏目' => 'require|gt:0',
+ 'title|标题' => 'require|max:255',
+ 'summary|摘要' => 'max:255',
+ 'content|内容' => 'require',
+ ], ['category_id.gt' => '请选择栏目']);
+
+ if ($validate !== true) {
+ return $validate;
+ }
+
+ try {
+ $now = date('Y-m-d H:i:s');
+ $item['created_at'] = $now;
+ $item['published_at'] = $now;
+ $item['created_by'] = $this->auth['user_id'];
+ ArchivesModel::create($item);
+
+ return $this->json();
+ } catch (ValidateException $e) {
+ return $this->json(4001, $e->getError());
+ }
+ }
+
+ $showFieldList = ArchivesModelField::showFieldList();//所有栏目 可展示字段列表
+ //有指定栏目获取指定栏目 否则获取第一个 可展示字段列表
+ $currentShowFields = $categoryId > 0 ? ($showFieldList[$categoryId] ?? []) : array_values($showFieldList)[0];
+
+ $this->data['categoryId'] = $categoryId ?? 0;
+ $this->data['diaryId'] = $diaryId ?? 0;
+ $this->data['jsonList'] = $this->xmSelectJson([$categoryId]);
+ $this->data['showList'] = json_encode($showFieldList, JSON_UNESCAPED_UNICODE);
+ $this->data['currentList'] = $currentShowFields;
+
+ return $this->view();
+ }
+
+ /**
+ * 列表
+ *
+ * @return View|Json
+ * @throws Exception
+ */
+ public function index()
+ {
+ $categoryId = input('category_id/d', 0);
+ if ($this->request->isPost()) {
+ $page = input('page/d', 1);
+ $limit = input('size/d', 20);
+ $searchParams = input('searchParams');
+ $where = [];
+
+ if ($categoryId > 0) {
+ $where[] = ['category_id', '=', $categoryId];
+ }
+
+ if ($searchParams) {
+ foreach ($searchParams as $key => $param) {
+ if (!empty($param)) {
+ if (is_string($param)) {
+ $where[] = [$key, 'like', '%'.$param.'%'];
+ } elseif (is_array($param)) {
+ //数组空元素去除
+ foreach ($param as $k => $val) {
+ if (empty($val)) {
+ unset($param[$k]);
+ }
+ }
+ if (!empty($param)) {
+ $where[] = [$key, 'in', $param];
+ }
+ }
+ }
+ }
+ }
+
+ $items = ArchivesModel::findList($where, [], $page, $limit, function ($q) {
+ return $q->with(['member', 'category'])
+ ->order('sort', 'desc')
+ ->order('id', 'desc');
+ });
+
+
+
+ return $this->json(0, '操作成功', $items);
+ }
+
+ $selected = $categoryId > 0 ? [$categoryId] : [];
+
+ $this->data['categoryId'] = $categoryId;
+ $this->data['categoryJson'] = $this->categoryJson($selected);
+ $this->data['archivesPath'] = '/'.Config::MINI_PATH_ARCHIVES;
+
+ return $this->view();
+ }
+
+ /**
+ * 构造分类 json数据[zTree用]
+ *
+ * @param array $selected
+ * @return false|string
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ */
+ private function categoryJson(array $selected = [])
+ {
+ $category = ArticleCategoryModel::order('sort', 'desc')
+ ->field('id,pid,title')
+ ->select()
+ ->toArray();
+ foreach ($category as $k => $m) {
+ $category[$k]['checked'] = in_array($m['id'], $selected);
+ $category[$k]['spread'] = true;
+ }
+
+ $category = CmsRepository::getInstance()->buildMenuChild(0, $category);
+ return json_encode($category, JSON_UNESCAPED_UNICODE);
+ }
+
+ /**
+ * 内容分类 构造xmSelect json数据[xmSelect用]
+ *
+ * @param array $selected
+ * @param array $disabled
+ * @return false|string
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ */
+ private function xmSelectJson(array $selected = [], array $disabled = [])
+ {
+ $category = ArticleCategoryModel::order('sort', 'desc')
+ ->field('id,pid,title')
+ ->select()
+ ->toArray();
+ foreach ($category as $k => $m) {
+ $category[$k]['selected'] = in_array($m['id'], $selected);
+ $category[$k]['disabled'] = in_array($m['id'], $disabled);
+ }
+
+ $category = CmsRepository::getInstance()->buildMenuChild(0, $category);
+ $category = CmsRepository::getInstance()->handleSelectedList($category);
+ return json_encode($category, JSON_UNESCAPED_UNICODE);
+ }
+}
\ No newline at end of file
diff --git a/app/controller/manager/ArchivesCategory.php b/app/controller/manager/ArchivesCategory.php
new file mode 100644
index 0000000..9fb9a46
--- /dev/null
+++ b/app/controller/manager/ArchivesCategory.php
@@ -0,0 +1,256 @@
+request->isPost()) {
+ $ids = input('post.ids/a', []);
+ if (empty($ids)) {
+ $ids[] = input('post.id/d');
+ }
+ if (ArchivesCategoryModel::hasChildrenByIds($ids)) {
+ return $this->json(4002, '待删除数据存在子数据');
+ }
+
+ if (ArchivesCategoryModel::hasContentByIds($ids)) {
+ return $this->json(4002, '待删除数据存在内容文章');
+ }
+ ArchivesCategoryModel::deleteByIds($ids);
+// Log::write(get_class().'Del', 'del', '涉及到的ID为:'.implode(',', $ids));
+ return $this->json();
+ }
+ return $this->json(4001, '非法请求!');
+ }
+
+ /**
+ * 编辑
+ *
+ * @return Json|View
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ * @throws Exception
+ */
+ public function edit()
+ {
+ Cache::delete("categoryNames");//删除缓存
+ $id = input('id/d', 0);
+
+ if (!$info = ArchivesCategoryModel::findById($id)) {
+ return $this->json(4001, '记录不存在');
+ }
+
+ if ($this->request->isPost()) {
+ $item = input('post.');
+ $validate = $this->validateByApi($item, [
+ 'pid|父级分类' => 'require|number',
+ 'model_id|所属模型' => 'require|number|gt:0',
+ 'title|标题' => 'require|max:100',
+ 'name|标识' => 'unique:archives_category,name,'.$info['id'] ?? 0,
+ 'description|描述' => 'max:255',
+ ], ['model_id' => '所属模型必需选择']);
+
+ if ($validate !== true) {
+ return $validate;
+ }
+
+ Db::startTrans();
+ try {
+ $oldPath = $info['path'] ?? '';
+ $item['path'] = ArchivesCategoryModel::getPath($item['pid']);
+ $info->save($item);
+
+ //刷新所有路径
+ $oldPath = $oldPath.','.$id;
+ $newPath = $item['path'].','.$id;
+ if ($oldPath != $newPath) {
+ ArchivesCategoryModel::refreshPath();
+ }
+ Db::commit();
+ return $this->json();
+ } catch (ValidateException $e) {
+ Db::rollback();
+ return $this->json(4001, $e->getError());
+ }
+ }
+
+ $disabled = ArchivesCategoryModel::getAllChildrenIds($id);
+ $disabled[] = $id;
+ $this->data['jsonList'] = $this->categoryJson([$info['pid']], $disabled);
+ $this->data['modelList'] = $this->modelJson([$info['model_id']], []);
+ $this->data['item'] = $info;
+
+ return $this->view();
+ }
+
+ /**
+ * 单个字段编辑
+ *
+ * @return Json
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ */
+ public function modify(): Json
+ {
+ Cache::delete("categoryNames");//删除缓存
+ if ($this->request->isPost()) {
+ $item = input('post.');
+ $validate = new MenuValidate();
+ if (!$validate->scene('menu_modify')->check($item)) {
+ return $this->json(4002, $validate->getError());
+ }
+
+ if (!$info = ArchivesCategoryModel::findById($item['id'])) {
+ return $this->json(4001, '记录不存在');
+ }
+
+ $update = [$item['field'] => $item['value']];
+
+ try {
+ $info->save($update);
+ return $this->json();
+ } catch (ValidateException $e) {
+ return $this->json(4001, $e->getError());
+ }
+ }
+ return $this->json(4000, '非法请求');
+ }
+
+ /**
+ * 添加
+ *
+ * @return Json|View
+ * @throws Exception
+ */
+ public function add()
+ {
+ Cache::delete("categoryNames");//删除缓存
+ if ($this->request->isPost()) {
+ $item = input('post.');
+ $validate = $this->validateByApi($item, [
+ 'pid|父级分类' => 'require|number',
+ 'model_id|所属模型' => 'require|number|gt:0',
+ 'title|标题' => 'require|max:100',
+ 'name|标识' => 'require|unique:archives_category',
+ 'description|描述' => 'max:255',
+ ], ['model_id' => '所属模型必需选择']);
+
+ if ($validate !== true) {
+ return $validate;
+ }
+ try {
+ $item['path'] = ArchivesCategoryModel::getPath($item['pid']);
+ ArchivesCategoryModel::create($item);
+ return $this->json();
+ } catch (ValidateException $e) {
+ return $this->json(4001, $e->getError());
+ }
+ }
+
+ $this->data['jsonList'] = $this->categoryJson();
+ $this->data['modelList'] = $this->modelJson();
+
+ return $this->view();
+ }
+
+ /**
+ * 列表
+ *
+ * @return Json|View
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ */
+ public function index()
+ {
+ if ($this->request->isPost()) {
+ $menus = ArchivesCategoryModel::getList();
+ $res = [
+ 'code' => 0,
+ 'msg' => 'success',
+ 'count' => $menus->count(),
+ 'data' => $menus->toArray(),
+ ];
+ return json($res);
+ }
+ return $this->view();
+ }
+
+ /**
+ * @param array $selected
+ * @param array $disabled
+ * @return false|string
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ */
+ private function categoryJson(array $selected = [], array $disabled = [])
+ {
+ $categoryList[] = ['title' => '顶级分类', 'id' => 0, 'disabled' => false, 'selected' => in_array(0, $selected)];
+ $menus = ArchivesCategoryModel::getList();
+ $menus = $menus->toArray();
+ foreach ($menus as $k => $m) {
+ $menus[$k]['selected'] = in_array($m['id'], $selected);
+ $menus[$k]['disabled'] = in_array($m['id'], $disabled);
+ }
+ $menus = CmsRepository::getInstance()->buildMenuChild(0, $menus);
+ $categoryList = array_merge($categoryList, CmsRepository::getInstance()->handleSelectedList($menus));
+ return json_encode($categoryList, JSON_UNESCAPED_UNICODE);
+ }
+
+ /**
+ * @param array $selected
+ * @param array $disabled
+ * @return false|string
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ */
+ private function modelJson(array $selected = [], array $disabled = [])
+ {
+ $categoryList[] = ['title' => '全部', 'id' => 0, 'disabled' => false, 'selected' => in_array(0, $selected)];
+ $menus = \app\model\ArchivesModel::field('id,0 as pid,title,name,sort,true as open')
+ ->order('sort', 'desc')
+ ->order('id', 'asc')
+ ->select();;
+ $menus = $menus->toArray();
+ foreach ($menus as $k => $m) {
+ $menus[$k]['selected'] = in_array($m['id'], $selected);
+ $menus[$k]['disabled'] = in_array($m['id'], $disabled);
+ }
+ $menus = CmsRepository::getInstance()->buildMenuChild(0, $menus);
+ $categoryList = array_merge($categoryList, CmsRepository::getInstance()->handleSelectedList($menus));
+ return json_encode($categoryList, JSON_UNESCAPED_UNICODE);
+ }
+}
\ No newline at end of file
diff --git a/app/controller/manager/ArchivesModel.php b/app/controller/manager/ArchivesModel.php
new file mode 100644
index 0000000..dd89032
--- /dev/null
+++ b/app/controller/manager/ArchivesModel.php
@@ -0,0 +1,173 @@
+request->isPost()) {
+ $ids = input('post.ids/a', []);
+ if (empty($ids)) {
+ $ids[] = input('post.id/d');
+ }
+ $groupNames = MArchivesModel::whereIn('id', $ids)->column('name');
+ if (ArchivesModelField::whereIn('name', $groupNames)->count() > 0) {
+ return $this->json(4002, '模型下已存在字段,无法删除!');
+ }
+
+ MArchivesModel::deleteByIds($ids);
+
+// Log::write(get_class().'Del', 'del', '涉及到的ID为:'.implode(',', $ids));
+ return $this->json();
+ }
+ return $this->json(4001, '非法请求!');
+ }
+
+ /**
+ * 编辑
+ *
+ * @return Json|View
+ * @throws Exception
+ */
+ public function edit()
+ {
+ $id = input('id/d', 0);
+
+ if (!$info = MArchivesModel::findById($id)) {
+ return $this->json(4001, '记录不存在');
+ }
+
+ if ($this->request->isPost()) {
+ $item = input('post.');
+ $validate = $this->validateByApi($item, [
+ 'title|模型标题' => 'require',
+ 'name|模型标识' => 'alphaDash|unique:archives_model,name,'.$id,
+ ]);
+
+ if ($validate !== true) {
+ return $validate;
+ }
+
+ try {
+ ArchivesModelField::setFieldList($id, $info['name']);
+ $info->save($item);
+ return $this->json();
+ } catch (ValidateException $e) {
+ return $this->json(4001, $e->getError());
+ }
+ }
+
+ $this->data['item'] = $info;
+
+ return $this->view();
+ }
+
+ /**
+ * 单个字段编辑
+ *
+ * @return Json
+ * @throws Exception
+ */
+ public function modify(): Json
+ {
+ if ($this->request->isPost()) {
+ $item = input('post.');
+ $validate = $this->validateByApi($item, [
+ 'field' => 'require',
+ 'value' => 'require',
+ ]);
+
+ if ($validate !== true) {
+ return $validate;
+ }
+
+ if (!$info = MArchivesModel::findById($item['id'])) {
+ return $this->json(4001, '记录不存在');
+ }
+
+ $update = [$item['field'] => $item['value']];
+
+ try {
+ $info->save($update);
+ return $this->json();
+ } catch (ValidateException $e) {
+ return $this->json(4001, $e->getError());
+ }
+ }
+ return $this->json(4000, '非法请求');
+ }
+
+ /**
+ * 添加
+ *
+ * @return Json|View
+ * @throws Exception
+ */
+ public function add()
+ {
+ if ($this->request->isPost()) {
+ $item = input('post.');
+
+ $validate = $this->validateByApi($item, [
+ 'title|模型标题' => 'require',
+ 'name|模型标识' => 'require|alphaDash|unique:archives_model',
+ ]);
+
+ if ($validate !== true) {
+ return $validate;
+ }
+
+ try {
+ $model = MArchivesModel::create($item);
+ ArchivesModelField::setFieldList($model['id'], $model['name']);
+ return $this->json();
+ } catch (ValidateException $e) {
+ return $this->json(4001, $e->getError());
+ }
+ }
+
+ return $this->view();
+ }
+
+ /**
+ * 列表
+ *
+ * @return View|Json
+ * @throws Exception
+ */
+ public function index()
+ {
+ if ($this->request->isPost()) {
+ $page = input('page/d', 1);
+ $limit = input('size/d', 20);
+ $items = MArchivesModel::findList([], [], $page, $limit, function ($q) {
+ return $q->order('sort', 'desc')->order('id', 'asc');
+ });
+
+ return $this->json(0, '操作成功', $items);
+ }
+ return $this->view();
+ }
+}
\ No newline at end of file
diff --git a/app/controller/manager/ArchivesModelField.php b/app/controller/manager/ArchivesModelField.php
new file mode 100644
index 0000000..f7c849c
--- /dev/null
+++ b/app/controller/manager/ArchivesModelField.php
@@ -0,0 +1,210 @@
+request->isPost()) {
+ $ids = input('post.ids/a', []);
+ if (empty($ids)) {
+ $ids[] = input('post.id/d');
+ }
+
+ MArchivesModelField::deleteByIds($ids);
+
+// Log::write(get_class().'Del', 'del', '涉及到的ID为:'.implode(',', $ids));
+ return $this->json();
+ }
+ return $this->json(4001, '非法请求!');
+ }
+
+ /**
+ * 编辑
+ *
+ * @return Json|View
+ * @throws Exception
+ */
+ public function edit()
+ {
+ $id = input('id/d', 0);
+
+ if (!$info = MArchivesModelField::findById($id)) {
+ return $this->json(4001, '记录不存在');
+ }
+
+ if ($this->request->isPost()) {
+ $item = input('post.');
+ $validate = $this->validateByApi($item, [
+ 'title|模型标题' => 'require',
+ 'name|模型标识' => 'alphaDash',
+ ]);
+
+ if ($validate !== true) {
+ return $validate;
+ }
+
+ try {
+ $info->save($item);
+ return $this->json();
+ } catch (ValidateException $e) {
+ return $this->json(4001, $e->getError());
+ }
+ }
+
+ $this->data['item'] = $info;
+
+ return $this->view();
+ }
+
+ /**
+ * 单个字段编辑
+ *
+ * @return Json
+ * @throws Exception
+ */
+ public function modify(): Json
+ {
+ if ($this->request->isPost()) {
+ $item = input('post.');
+ $validate = $this->validateByApi($item, [
+ 'field' => 'require',
+ 'value' => 'require',
+ ]);
+
+ if ($validate !== true) {
+ return $validate;
+ }
+
+ if (!$info = MArchivesModelField::findById($item['id'])) {
+ return $this->json(4001, '记录不存在');
+ }
+
+ $update = [$item['field'] => $item['value']];
+
+ try {
+ $info->save($update);
+ return $this->json();
+ } catch (ValidateException $e) {
+ return $this->json(4001, $e->getError());
+ }
+ }
+ return $this->json(4000, '非法请求');
+ }
+
+ /**
+ * 添加
+ *
+ * @return Json|View
+ * @throws Exception
+ */
+ public function add()
+ {
+ $modelId = input('model_id/d', 0);
+
+ if ($this->request->isPost()) {
+ $item = input('post.');
+ $item['model_id'] = $modelId;
+
+ $validate = $this->validateByApi($item, [
+ 'title|字段标题' => 'require',
+ 'model_id|模型' => 'require|number|gt:0',
+ 'name|字段标识' => 'require|alphaDash|unique:archives_model_field',
+ ], ['model_id.gt' => '模型不存在']);
+
+ if ($validate !== true) {
+ return $validate;
+ }
+
+ try {
+ MArchivesModelField::create($item);
+ return $this->json();
+ } catch (ValidateException $e) {
+ return $this->json(4001, $e->getError());
+ }
+ }
+
+ $this->data['modelId'] = $modelId;
+
+ return $this->view();
+ }
+
+ /**
+ * 列表
+ *
+ * @return View|Json
+ * @throws Exception
+ */
+ public function index()
+ {
+ $modelId = input('model_id/d', 0);
+ if (!$modelId) {
+ return $this->json(4001, '请选择正确的模型');
+ }
+
+ if ($this->request->isPost()) {
+ $page = input('page/d', 1);
+ $limit = input('size/d', 20);
+ $where[] = ['model_id', '=', $modelId];
+ $items = MArchivesModelField::findList($where, [], $page, $limit, function ($q) {
+ return $q->order('status', 'desc')->order('id', 'asc');
+ });
+
+ return $this->json(0, '操作成功', $items);
+ }
+
+ $this->data['modelId'] = $modelId;
+
+ return $this->view();
+ }
+
+ /**
+ * 同步字段
+ *
+ * @return Json
+ * @throws Exception
+ */
+ public function sync(): Json
+ {
+ if ($this->request->isPost()) {
+ $modelId = input('model_id/d', 0);
+
+ if (!$modelId) {
+ return $this->json(4001, '模型错误');
+ }
+
+
+ if (!$info = ArchivesModel::findOne(['id' => $modelId])) {
+ return $this->json(4001, '模型不存在');
+ }
+
+ try {
+ MArchivesModelField::syncFieldList($modelId, $info['name']);
+ return $this->json();
+ } catch (ValidateException $e) {
+ return $this->json(4001, $e->getError());
+ }
+ }
+ return $this->json(4000, '非法请求');
+ }
+}
\ No newline at end of file
diff --git a/app/controller/manager/Attachment.php b/app/controller/manager/Attachment.php
new file mode 100644
index 0000000..1f9a82c
--- /dev/null
+++ b/app/controller/manager/Attachment.php
@@ -0,0 +1,488 @@
+DIRECTORY_SEPARATOR = DIRECTORY_SEPARATOR == "\\" ? "/" : DIRECTORY_SEPARATOR;
+ }
+
+ /**
+ * 删除
+ *
+ * @return Json
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ */
+ public function del(): Json
+ {
+ if ($this->request->isPost()) {
+ $ids = input('post.ids/a', []);
+ if (empty($ids)) {
+ $ids[] = input('post.id/d');
+ }
+
+ $items = AttachmentModel::whereIn('id', $ids)->where('is_dir', AttachmentModel::COMMON_ON)->select();
+ if ($items->where('is_dir', AttachmentModel::COMMON_ON)->count()) {
+ $dirPaths = [];
+ foreach ($items->toArray() as $item) {
+ $dirPaths[] = $item['path'].$item['name']. $this->DIRECTORY_SEPARATOR ;
+ }
+ if (AttachmentModel::where('path', 'in', $dirPaths)->count()) {
+ return $this->json(4001, '待删除目录下存在内容!');
+ }
+ }
+ AttachmentModel::deleteByIds($ids);
+
+// Log::write(get_class().'Del', 'del', '涉及到的ID为:'.implode(',', $ids));
+ return $this->json();
+ }
+ return $this->json(4001, '非法请求!');
+ }
+
+ /**
+ * 单个字段编辑
+ *
+ * @return Json
+ * @throws Exception
+ */
+ public function modify(): Json
+ {
+ if ($this->request->isPost()) {
+ $item = input('post.');
+ $validate = $this->validateByApi($item, [
+ 'field' => 'require',
+ 'value' => 'require',
+ ]);
+
+ if ($validate !== true) {
+ return $validate;
+ }
+
+ if (!$info = AttachmentModel::findById($item['id'])) {
+ return $this->json(4001, '记录不存在');
+ }
+
+ if ($item['field'] == 'name' && $info['is_dir'] == AttachmentModel::COMMON_ON) {
+ return $this->json(4002, '目录名称不能修改');
+
+ }
+
+ $update = [$item['field'] => $item['value']];
+
+ try {
+ $info->save($update);
+ return $this->json();
+ } catch (ValidateException $e) {
+ return $this->json(4001, $e->getError());
+ }
+ }
+ return $this->json(4000, '非法请求');
+ }
+
+ /**
+ * 添加
+ *
+ * @return Json|View
+ * @throws Exception
+ */
+ public function add()
+ {
+ if ($this->request->isPost()) {
+ $item = input('post.');
+
+ $validate = $this->validateByApi($item, [
+ 'title|合集标题' => 'require',
+ 'user|虚拟用户' => 'require',
+ 'headimg|虚拟用户头像' => 'require',
+ ]);
+
+ if ($validate !== true) {
+ return $validate;
+ }
+
+ try {
+ $now = date('Y-m-d H:i:s');
+ $item['created_at'] = $now;
+ $item['created_by'] = $this->auth['user_id'];
+ AttachmentModel::create($item);
+ return $this->json();
+ } catch (ValidateException $e) {
+ return $this->json(4001, $e->getError());
+ }
+ }
+
+ return $this->view();
+ }
+
+ /**
+ * 添加文件夹
+ *
+ * @return Json|View
+ * @throws Exception
+ */
+ public function addFolder()
+ {
+ if ($this->request->isPost()) {
+ $item = input('post.');
+
+ $validate = $this->validateByApi($item, [
+ 'name|文件夹名称' => 'require|alphaDash|max:20',
+ 'path|文件路径' => 'require',
+ ]);
+
+ // 例 name=dir4 path=/storage/dir1/dir2/dir3
+
+ // 去首尾/
+ $path = trim($item['path'], $this->DIRECTORY_SEPARATOR );
+ // 全路径 如 /storage/dir1/dir2/dir3/dir4/ 注意前后都有/
+ $fullPath = $this->DIRECTORY_SEPARATOR .$path. $this->DIRECTORY_SEPARATOR .$item['name']. $this->DIRECTORY_SEPARATOR ;
+
+ if ($validate !== true) {
+ return $validate;
+ }
+
+ AttachmentModel::pathDirHandle($fullPath);
+ return $this->json();
+ }
+
+ $path = input('path/s', AttachmentModel::ROOT_PATH);
+
+ $this->data['path'] = $path;
+ return $this->view();
+ }
+
+ /**
+ * 图片列表
+ *
+ * @return View|Json
+ * @throws Exception
+ */
+ public function image()
+ {
+ $path = input('post.path', AttachmentModel::ROOT_PATH);
+ $path = $this->DIRECTORY_SEPARATOR . trim($path, $this->DIRECTORY_SEPARATOR). $this->DIRECTORY_SEPARATOR;
+ $path = str_replace("\\","/",$path);
+ $type = input('type/s', 'image');
+ $selected = input('selected', false);
+ $multiple = input('multiple', false);
+
+ Config::load('extra/alioss', 'alioss');
+ $config = config('alioss');
+ $oss = $config['customDomain'];
+ if ($this->request->isPost()) {
+ $items = $this->list($path, ['image']);
+
+ return $this->json(0, '操作成功', $items);
+ }
+
+ $this->data['path'] = $path;
+ $this->data['oss'] = $oss;
+ $this->data['type'] = $type;
+ $this->data['multiple'] = $multiple;
+ $this->data['selected'] = $selected;
+ return $this->view();
+ }
+
+ /**
+ * 视频列表
+ *
+ * @return View|Json
+ * @throws Exception
+ */
+ public function video()
+ {
+ $path = input('post.path', AttachmentModel::ROOT_PATH);
+ $path = $this->DIRECTORY_SEPARATOR . trim($path, $this->DIRECTORY_SEPARATOR ) . $this->DIRECTORY_SEPARATOR ;
+ $path = str_replace("\\","/",$path);
+ $type = input('type/s', 'video');
+ $selected = input('selected', false);
+ $multiple = input('multiple', false);
+
+ Config::load('extra/alioss', 'alioss');
+ $config = config('alioss');
+ $oss = $config['customDomain'];
+ if ($this->request->isPost()) {
+ $items = $this->list($path, ['video']);
+
+ return $this->json(0, '操作成功', $items);
+ }
+
+ $this->data['path'] = $path;
+ $this->data['oss'] = $oss;
+ $this->data['type'] = $type;
+ $this->data['multiple'] = $multiple;
+ $this->data['selected'] = $selected;
+ return $this->view();
+ }
+
+ /**
+ * 文件列表
+ *
+ * @return View|Json
+ * @throws Exception
+ */
+ public function file()
+ {
+ Config::load('extra/alioss', 'alioss');
+ $config = config('alioss');
+ $oss = $config['customDomain'];
+ $type = input('type/s', 'all');
+ $selected = input('selected', false);
+ $multiple = input('multiple', false);
+
+ if ($this->request->isPost()) {
+ $page = input('post.page', 1);
+ $size = input('post.size', 20);
+ $searchParams = input('searchParams');
+ $where = [];
+
+ if ($searchParams) {
+ foreach ($searchParams as $key => $param) {
+ if (!empty($param)) {
+ if (is_string($param)) {
+ if ($key == 'is_oss') {
+ if ($param >= 0) {
+ $where[] = ['is_oss', '=', $param];
+ }
+ } else {
+ $where[] = [$key, 'like', '%'.$param.'%'];
+ }
+ } elseif (is_array($param)) {
+ //数组空元素去除
+ foreach ($param as $k => $val) {
+ if (empty($val)) {
+ unset($param[$k]);
+ }
+ }
+ if (!empty($param)) {
+ $where[] = [$key, 'in', $param];
+ }
+ }
+ }
+ }
+ }
+
+
+ if ($type !== 'all') {
+ $where[] = ['type', '=', $type];
+ }
+
+ $items = AttachmentModel::findList($where, [], $page, $size, function ($q) {
+ return $q->where('type', '<>', 'dir')->order('updated_at', 'desc');
+ });
+
+ $items['list']->each(function ($item) {
+ $item->size_text = getFilesize($item['size'] ?? 0);
+ });
+
+ return $this->json(0, '操作成功', $items);
+ }
+
+ $this->data['oss'] = $oss;
+ $this->data['type'] = $type;
+ $this->data['multiple'] = $multiple;
+ $this->data['selected'] = $selected;
+ return $this->view();
+ }
+
+ /**
+ * 一键删除失效记录 即oss不存在&&本地不存在
+ *
+ * @return Json
+ */
+ public function delLostFile(): Json
+ {
+ if ($this->request->isPost()) {
+ $total = AttachmentModel::where('type', '<>', AttachmentModel::TYPE_DIR)
+ ->where('is_dir', AttachmentModel::COMMON_OFF)
+ ->where('is_oss', AttachmentModel::COMMON_OFF)
+ ->where('has_local', AttachmentModel::COMMON_OFF)
+ ->count();
+ if ($total === 0) {
+ return $this->json(0, 'success', ['total' => $total]);
+ }
+
+ if (AttachmentModel::where('type', '<>', AttachmentModel::TYPE_DIR)
+ ->where('is_dir', AttachmentModel::COMMON_OFF)
+ ->where('is_oss', AttachmentModel::COMMON_OFF)
+ ->where('has_local', AttachmentModel::COMMON_OFF)
+ ->delete()) {
+ return $this->json(0, 'success', ['total' => $total]);
+ }
+ return $this->json(4004, '删除失败');
+ }
+ return $this->json(4000, '请求错误');
+ }
+
+ /**
+ * 一键上传本地文件到OSS
+ *
+ * @return Json
+ */
+ public function toOss(): Json
+ {
+ if ($this->request->isPost()) {
+ Config::load('extra/alioss', 'alioss');
+ $config = config('alioss');
+
+ $ossObject = AliOss::instance();
+ $bucket = $config['bucket'];
+
+ $total = AttachmentModel::where('type', '<>', AttachmentModel::TYPE_DIR)
+ ->where('is_dir', AttachmentModel::COMMON_OFF)
+ ->where('is_oss', AttachmentModel::COMMON_OFF)
+ ->field('id')
+ ->count();
+ $done = 0;
+ $none = 0;
+ if ($total === 0) {
+ return $this->json(0, 'success', ['total' => $total, 'done' => $done, 'none' => $none]);
+ }
+ try {
+ AttachmentModel::where('type', '<>', AttachmentModel::TYPE_DIR)
+ ->where('is_dir', AttachmentModel::COMMON_OFF)
+ ->where('is_oss', AttachmentModel::COMMON_OFF)
+ ->field('id,src')
+ ->chunk(3, function ($items) use ($ossObject, $bucket, &$done, &$none) {
+ $doneIds = [];
+ $noneIds = [];
+ foreach ($items as $item) {
+ if ($item['src']) {
+ $realPath = public_path().ltrim($item['src'], $this->DIRECTORY_SEPARATOR );
+ if (!file_exists($realPath)) {
+ $none++;
+ $noneIds[] = $item['id'];
+ continue;
+ }
+ $pathInfo = pathinfo($item['src']);
+ $object = ltrim($item['src'], $this->DIRECTORY_SEPARATOR );
+ //是否存在
+ if (!$ossObject->doesObjectExist($bucket, $object)) {
+ //创建目录
+ $ossObject->createObjectDir($bucket, ltrim($pathInfo['dirname'], $this->DIRECTORY_SEPARATOR ));
+
+ $ossObject->uploadFile($bucket, $object, $realPath);
+ }
+ $doneIds[] = $item['id'];
+ $done++;
+ }
+ }
+
+ // 失效标记
+ if ($noneIds) {
+ $update = ['is_oss' => AttachmentModel::COMMON_OFF, 'has_local' => AttachmentModel::COMMON_OFF];
+ (new AttachmentModel())->where('id', 'in', $noneIds)->update($update);
+ }
+
+ // 完成标记
+ if ($doneIds) {
+ $update = ['is_oss' => AttachmentModel::COMMON_ON];
+ (new AttachmentModel())->where('id', 'in', $doneIds)->update($update);
+ }
+ });
+
+ return $this->json(0, 'success', ['total' => $total, 'done' => $done, 'none' => $none]);
+ } catch (Exception $e) {
+ \think\facade\Log::error('本地文件一键上传OSS失败 '.$e->getMessage());
+ return $this->json(0, 'success', ['total' => $total, 'done' => $done, 'none' => $none]);
+ }
+ }
+ }
+
+ /**
+ * 指定类型附件列表
+ *
+ * @param array $type
+ * @param string $path
+ * @return array
+ * @throws Exception
+ */
+ protected function list(string $path, array $type): array
+ {
+ $type[] = 'dir';
+ $where[] = ['path', '=', $path];
+ $where[] = ['type', 'in', $type];
+ $items = AttachmentModel::findList($where, [], 1, 0, function ($q) {
+ return $q->order('is_dir', 'desc')->order('updated_at', 'desc');
+ });
+ $items['list']->each(function ($item) {
+ $item->size_text = getFilesize($item['size'] ?? 0);
+ });
+ $items['path'] = $path;
+
+ return $items;
+ }
+
+ /**
+ * 获取文件大小
+ *
+ * @return Json
+ */
+ public function getSize(): Json
+ {
+ $path = input('post.path', '');
+ $types = input('post.type/a', []);
+
+ $size = '';
+ if (empty($path)) {
+ return $this->json(0, '操作成功', $size);
+ }
+ $path = str_replace("\\","/",$path);
+ $total = AttachmentModel::where('path', 'like', $path.'%')
+ ->when(!empty($types), function ($q) use ($types) {
+ $q->where('type', 'in', $types);
+ })
+ ->sum('size');
+
+ return $this->json(0, '操作成功', getFilesize($total));
+ }
+
+ // 将没有md5的文件 更新md5 仅针对本地文件
+ public function md5List()
+ {
+ $noMd5List = AttachmentModel::whereNull('md5')->select();
+ $update = [];
+ foreach ($noMd5List as $item) {
+ try {
+ if (!empty($item['src'])) {
+ $arr = [];
+ $path = public_path().ltrim($item['src'], $this->DIRECTORY_SEPARATOR );
+ $file = new \think\File($path);
+ $arr['md5'] = $file->md5();
+ $arr['id'] = $item['id'];
+
+ $update[] = $arr;
+ }
+ } catch (Exception $e) {
+ continue;
+ }
+
+ }
+ (new AttachmentModel())->saveAll($update);
+ }
+}
\ No newline at end of file
diff --git a/app/controller/manager/Backup.php b/app/controller/manager/Backup.php
new file mode 100644
index 0000000..cdb0700
--- /dev/null
+++ b/app/controller/manager/Backup.php
@@ -0,0 +1,153 @@
+ 0) {
+ $dataStr = 'INSERT INTO `'.$tableName.'` VALUE ';
+ foreach ($data as $k => $item) {
+ $valStr = '(';
+ foreach ($item as $val) {
+ // 字符串转义
+ $val = addslashes($val);
+ $valStr .= '"'.$val.'", ';
+ }
+ // 去除最后的逗号和空格
+ $valStr = substr($valStr,0,strlen($valStr)-2);
+ // 限制单条sql语句在16K以内(可根据mysql配置条件进行调整)
+ $sqlLength = strlen($dataStr) + strlen($valStr);
+ if($sqlLength >= (1024 * 16 - 10)) {
+ $dataStr .= $valStr.');'.$eol;
+ file_put_contents($fileName, $dataStr, FILE_APPEND);
+ // 提前分段写入后需重置数据语句
+ if ($k <= ($count-1)) {
+ $dataStr = 'INSERT INTO `'.$tableName.'` VALUE ';
+ } else {
+ $dataStr = '';
+ }
+ } else {
+ if ($k < ($count-1)) {
+ $dataStr .= $valStr.'),'.$eol;
+ } else {
+ $dataStr .= $valStr.');'.$eolB;
+ }
+ }
+ }
+ file_put_contents($fileName, $dataStr, FILE_APPEND);
+ }
+ }
+ clearstatcache();
+
+ $backups = explode('/', $fileName);
+ $backupName = $backups[count($backups)-1];
+ $src = Config::get('filesystem.disks.backup.url') . '/';
+ return $this->json(0, '备份成功:' . $src . $backupName);
+ }
+
+
+ public function index()
+ {
+ $path = Config::get('filesystem.disks.backup.root') . '/';
+ $src = Config::get('filesystem.disks.backup.url') . '/';
+ $items = [];
+ if(is_dir($path)) {
+ if(!is_readable($path)) {
+ chmod($path,0755);
+ }
+ $files = scandir($path);
+ foreach ($files as $file) {
+ if($file != '.' && $file != '..') {
+ $ext = substr($file, -4);
+ if ($ext == '.sql') {
+ $creatTime = substr($file, -18, 14);
+ $items[] = [
+ 'file' => $file,
+ 'path' => $src . $file,
+ 'time' =>is_numeric($creatTime) ? date('Y-m-d H:i:s', strtotime($creatTime)) : '',
+ ];
+ }
+ }
+ }
+ }
+ clearstatcache();
+
+ $this->data['items'] = $items;
+ $this->data['backupPath'] = str_replace('\\','/', $path);
+ return $this->view();
+ }
+
+ public function del()
+ {
+ if (request()->isPost()) {
+ $filePath = input('post.id');
+ Tool::delFile($filePath);
+ MLog::write('backup', 'del', '删除了备份数据文件,文件路径为:' . $filePath);
+ return $this->json();
+ } else {
+ return $this->json(1, '非法请求');
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/controller/manager/Base.php b/app/controller/manager/Base.php
new file mode 100644
index 0000000..a9308e4
--- /dev/null
+++ b/app/controller/manager/Base.php
@@ -0,0 +1,105 @@
+middleware = [
+ 'auth' => ['except' => array_merge($this->noNeedLogin, $this->noNeedRight)],
+ 'log'
+ // 'jwt' => ['except' => $this->noNeedRight],
+ ];
+ $this->auth = session('auth');
+ $this->data['member'] = $this->auth;
+ $this->data['_token'] = $this->auth['token'] ?? '';
+ $this->data['groupId'] = $this->auth['groupId'] ?? 0;
+
+ $this->fileDomain();
+ }
+
+ //变量赋值到模板
+ protected function view(string $template = '')
+ {
+ return view($template)->assign($this->data);
+ }
+
+ /**
+ * @param string $msg
+ * @param string|null $url
+ * @param string $data
+ * @param int $wait
+ * @return Redirect
+ */
+ protected function error($msg = '', string $url = null, $data = '', int $wait = 3): Redirect
+ {
+ if (is_null($url)) {
+ $url = $this->request->isAjax() ? '' : 'javascript:history.back(-1);';
+ } elseif ($url) {
+ $url = (strpos($url, '://') || 0 === strpos($url, '/')) ? $url : $this->app->route->buildUrl($url);
+ }
+ $result = [
+ 'code' => 0,
+ 'msg' => $msg,
+ 'data' => $data,
+ 'url' => $url,
+ 'wait' => $wait,
+ ];
+
+ return $this->redirect(url('/manager/error/jump', $result));
+ }
+
+ public function __call($name, $args)
+ {
+ return $this->view('/manager/error/jump');
+ }
+
+ /**
+ * 验证器
+ *
+ * @param array $data
+ * @param $validate
+ * @param array $message
+ * @param bool $batch
+ * @return Redirect
+ * @throws Exception
+ */
+ protected function validateError(array $data, $validate, array $message = [], bool $batch = false): Redirect
+ {
+ try {
+ parent::validate($data, $validate, $message, $batch);
+ } catch (ValidateException $e) {
+ $msg = $e->getMessage();
+ if ($batch) {
+ $msg = implode(',', $e->getError());
+ }
+
+ return $this->error($msg);
+ } catch (Exception $e) {
+ throw $e;
+ }
+ }
+
+ /**
+ * 文件域名前缀
+ */
+ public function fileDomain()
+ {
+ $this->data['fileDomain'] = FileTool::getFileDomain();
+ }
+}
\ No newline at end of file
diff --git a/app/controller/manager/Block.php b/app/controller/manager/Block.php
new file mode 100644
index 0000000..7dfc544
--- /dev/null
+++ b/app/controller/manager/Block.php
@@ -0,0 +1,224 @@
+,开发者QQ群:50304283
+// +----------------------------------------------------------------------
+
+namespace app\controller\manager;
+use app\model\ArchivesCategory as ArticleCategoryModel;
+use app\model\ArchivesModelField;
+use app\model\Block as BlockModel;
+use app\model\System;
+use app\validate\Block as VBlock;
+use app\repository\CmsRepository;
+use think\db\exception\DataNotFoundException;
+use think\db\exception\DbException;
+use think\db\exception\ModelNotFoundException;
+use think\exception\ValidateException;
+
+
+/**
+ * 碎片控制器
+ * @package app\controller\cms
+ */
+class Block extends Base
+{
+
+ protected function initialize()
+ {
+ parent::initialize();
+ $action = $this->request->action();
+ $cid = $this->request->param('cid/d');
+ if (($action == 'add' || $action == 'edit') && !$this->request->isPost()) {
+
+ $showFieldList = ArchivesModelField::showFieldList();//所有栏目 可展示字段列表
+ $currentShowFields = $showFieldList[$cid] ?? [];//当前选中栏目 可展示字段列表
+
+ $this->data['system'] = System::getSystem();
+
+ $this->data['currentList'] = $currentShowFields;
+ }
+ $this->data['jsonList'] = $this->xmSelectJson([$cid]);
+
+ }
+
+ public function index()
+ {
+ if ($this->request->isAjax()) {
+ $page = input('page/d', 1);
+ $limit = input('size/d', 20);
+ $keyword = input('searchParams.keyword/s');
+ $categoryId = input('searchParams.category_id/d', 0);
+ $where = [];
+
+ if ($categoryId > 0) {
+ $where[] = ['category_id', '=', $categoryId];
+ }
+
+ if (!empty($keyword)) {
+ $where[] = ["name|title", 'like', '%'.$keyword.'%'];
+ }
+
+ $items = BlockModel::findList($where, [], $page, $limit, function ($q) {
+ return $q->with(["category"])->withAttr("content",function ($value,$data){
+ switch ($data["type"]){
+ case BlockModel::BLOCK://
+ return $value;
+ break;
+ case BlockModel::TEXT:
+ return $value;
+ break;
+ case BlockModel::IMG:
+ return "";
+ break;
+ case BlockModel::GROUP:
+ $data = explode(",",$value);
+ $str = "";
+ foreach ($data as $vdata){
+ $str.="";
+ }
+ return $str;
+ case BlockModel::FILE:
+ return $value;
+ break;
+ case BlockModel::VIDEO:
+ return $value;
+ break;
+ case BlockModel::ING_LIST:
+ return $value;
+ break;
+ }
+ });
+ },["id"=>"desc"]);
+
+ return $this->json(0, '操作成功', $items);
+
+ }
+
+ return $this->view();
+ }
+
+ public function add(){
+ if ($this->request->isPost()) {
+ $postData = $this->request->post();
+ try {
+ $this->validate($postData, VBlock::class);
+ $postData["content"] = $postData["content".$postData["type"]];
+
+ $hasWhere =[["name","=",$postData["name"]]] ;
+ if( $postData["category_id"] > 0 ){
+ $hasWhere[] = ["category_id","=",$postData["category_id"]];
+
+ }
+ $other = BlockModel::findOne($hasWhere);
+ if(!empty($other)){
+ throw new ValidateException("键值重复");
+ }
+ }catch (ValidateException $e){
+ return $this->json(4001,$e->getError());
+ }
+
+
+ try {
+
+ BlockModel::create($postData);
+ return $this->json();
+ }catch (\Exception $e){
+ return $this->json(0,'保存失败'. $e->getMessage());
+ }
+ }
+ $this->data["types"] = BlockModel::getTypes();
+ return $this->view();
+ }
+
+ public function edit(){
+ $id = input("id/d");
+ $item = BlockModel::findById($id);
+ if ($this->request->isPost()) {
+ $postData = $this->request->post();
+ try {
+ $this->validate($postData, VBlock::class);
+ $postData["content"] = $postData["content".$postData["type"]];
+ $hasWhere =[["name","=",$postData["name"]],["id","<>",$id]] ;
+ if( $postData["category_id"] > 0 ){
+ $hasWhere[] = ["category_id","=",$postData["category_id"]];
+ }else{
+ $hasWhere[] = ["category_id","=",0];
+ }
+ $other = BlockModel::findOne($hasWhere);
+ if(!empty($other)){
+ throw new ValidateException("键值重复");
+ }
+ }catch (ValidateException $e){
+ return $this->json(4001,$e->getError());
+ }
+ try {
+ $item->save($postData);
+ return $this->json();
+ }catch (\Exception $e){
+ return $this->json(0,'保存失败'. $e->getMessage());
+ }
+ }
+
+ if(empty($item)){
+ return $this->error("碎片不存在");
+ }
+ $item =$item->toArray();
+ if($item['type'] == BlockModel::ING_LIST){
+ $contentKey = array_keys($item['content']);
+ $this->data['maxKey'] = max($contentKey)+1;
+ }else{
+ $this->data['maxKey'] = 0;
+ }
+ $this->data["item"] = $item;
+
+ $this->data["types"] = BlockModel::getTypes();
+ return $this->view();
+ }
+
+
+ /**
+ * 删除
+ *
+ * @return Json
+ */
+ public function del()
+ {
+ if ($this->request->isPost()) {
+ $id = input('id/d', 0);
+ BlockModel::deleteById($id);
+ return $this->json();
+ }
+ return $this->json(4001, '非法请求!');
+ }
+ /**
+ * 内容分类 构造xmSelect json数据[xmSelect用]
+ *
+ * @param array $selected
+ * @param array $disabled
+ * @return false|string
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ */
+ private function xmSelectJson(array $selected = [], array $disabled = [])
+ {
+ $category = ArticleCategoryModel::order('sort', 'desc')
+ ->field('id,pid,title')
+ ->select()
+ ->toArray();
+ foreach ($category as $k => $m) {
+ $category[$k]['selected'] = in_array($m['id'], $selected);
+ $category[$k]['disabled'] = in_array($m['id'], $disabled);
+ }
+
+ $category = CmsRepository::getInstance()->buildMenuChild(0, $category);
+ $category = CmsRepository::getInstance()->handleSelectedList($category);
+ return json_encode($category, JSON_UNESCAPED_UNICODE);
+ }
+}
\ No newline at end of file
diff --git a/app/controller/manager/Config.php b/app/controller/manager/Config.php
new file mode 100644
index 0000000..471691c
--- /dev/null
+++ b/app/controller/manager/Config.php
@@ -0,0 +1,85 @@
+extraPath = config_path() . 'extra/';
+ if (!is_dir($this->extraPath)) {
+ if (is_writable(config_path())) {
+ mkdir($this->extraPath, 0777, true);
+ } else {
+ halt('请联系系统管理人员配置文件夹读写权限!请添加'.$this->extraPath.'文件夹的读写权限');
+ }
+ } elseif (!is_writable($this->extraPath)) {
+ halt('请联系系统管理人员配置文件夹读写权限!请添加'.$this->extraPath.'文件夹的读写权限');
+ }
+ }
+
+
+
+ public function wechat()
+ {
+ if ($this->request->isPost()) {
+ $data = input("post.");
+ unset($data['_token']);
+ $php = var_export($data, true);
+ file_put_contents($this->extraPath . 'wechat.php', 'json();
+ } else {
+ CConfig::load('extra/wechat', 'wechat');
+ $this->data['item'] = config('wechat');
+ return $this->view();
+ }
+ }
+
+ public function alipay()
+ {
+ if ($this->request->isPost()) {
+ $data = input("post.");
+ unset($data['_token']);
+ $php = var_export($data, true);
+ file_put_contents($this->extraPath . 'alipay.php', 'json();
+ } else {
+ CConfig::load('extra/alipay', 'alipay');
+ $this->data['item'] = config('alipay');
+ return $this->view();
+ }
+ }
+
+
+ public function __call($name, $args)
+ {
+ if ($this->request->isPost()) {
+ try {
+ $data = input("post.");
+ $php = var_export($data, true);
+ file_put_contents(config_path().'extra/'.$name.'.php', 'json();
+ } catch (Exception $e) {
+ return $this->json(4001, $e->getMessage());
+ }
+ } else {
+ CConfig::load('extra/'.$name, $name);
+ $this->data['item'] = config($name);
+ $this->data['action'] = $name;
+ return $this->view('manager/config/'.$name);
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/controller/manager/Error.php b/app/controller/manager/Error.php
new file mode 100644
index 0000000..a10fc30
--- /dev/null
+++ b/app/controller/manager/Error.php
@@ -0,0 +1,11 @@
+param();
+ return view()->assign($param);
+ }
+}
diff --git a/app/controller/manager/Feedback.php b/app/controller/manager/Feedback.php
new file mode 100644
index 0000000..6bf1a96
--- /dev/null
+++ b/app/controller/manager/Feedback.php
@@ -0,0 +1,82 @@
+request->isPost()) {
+ $ids = input('post.ids/a', []);
+ if (empty($ids)) {
+ $ids[] = input('post.id/d');
+ }
+ FeedbackModel::deleteByIds($ids);
+ Log::write(get_class().'Del', 'del', '涉及到的ID为:'.implode(',', $ids));
+ return $this->json();
+ }
+ return $this->json(4001, '非法请求!');
+ }
+
+ /**
+ * 列表
+ *
+ * @return View|Json
+ * @throws Exception
+ */
+ public function index()
+ {
+ if ($this->request->isPost()) {
+ $page = input('page/d', 1);
+ $limit = input('size/d', 20);
+ $searchParams = input('searchParams', []);
+ $search = [];
+ if ($searchParams) {
+ $searchParams = array_map('trim', $searchParams);
+ if (isset($searchParams['user_keyword']) && !empty($searchParams['user_keyword'])) {
+ $search[] = ['user_name|user_tel|user_email', 'like', '%'.$searchParams['user_keyword'].'%'];
+ }
+ unset($searchParams['user_keyword']);
+
+ foreach ($searchParams as $key => $param) {
+ if ($param) {
+ $search[] = [$key, 'like', '%'.$param.'%'];
+ }
+ }
+ }
+
+ $items = FeedbackModel::findList($search, [], $page, $limit, function ($q) {
+ return $q->with(['account'])->order('id', 'desc');
+ });
+
+ $items['list'] = $items['list']->each(function ($item) {
+ if ($item->account) {
+ $item->user_name = empty($item->user_name) ? ($item->account->nickname ?? '') : $item->user_name;
+ $item->user_tel = empty($item->user_tel) ? ($item->account->mobile ?? '') : $item->user_tel;
+ }
+ });
+
+ return $this->json(0, '操作成功', $items);
+ }
+
+
+ return $this->view();
+ }
+}
\ No newline at end of file
diff --git a/app/controller/manager/File.php b/app/controller/manager/File.php
new file mode 100644
index 0000000..73de0b8
--- /dev/null
+++ b/app/controller/manager/File.php
@@ -0,0 +1,143 @@
+request->isPost()) {
+ $paths = input('post.paths/a');
+ if(!empty($paths)){
+ foreach($paths as $path){
+ Tool::delFile($path);
+ }
+ Log::write('file', 'delPath', '批量删除了磁盘文件,涉及到的文件路径为:' . implode(',', $paths));
+ return $this->json();
+ }
+ return $this->json(2, '待删除文件列表为空');
+ }
+ return $this->json(1, '非法请求!');
+ }
+ //删除文件记录
+ public function del()
+ {
+ if ($this->request->isPost()) {
+ $ids = input('post.ids/a');
+ if(empty($ids) || !is_array($ids)) {
+ return $this->json(2, '参数错误,请核对之后再操作!');
+ }
+ $items = MFile::getListByIds($ids);
+ if(!empty($items)){
+ $delIds = [];
+ foreach($items as $item){
+ $delIds[] = $item['id'];
+ if($item['type'] == MFile::IMG){
+ Tool::delFile($item['src'], 1);
+ }else{
+ Tool::delFile($item['src']);
+ }
+ }
+ MFile::destroy($delIds);
+ Log::write('file', 'del', '批量删除了文件,涉及到的文件ID为:' . implode(',', $delIds));
+ return $this->json();
+ }else{
+ return $this->json(3, '待删除文件列表为空');
+ }
+ }
+ return $this->json(1, '非法请求!');
+ }
+ /**
+ * 未使用文件列表,
+ * 1. 遍历数据库中使用的图片视频及文件路径
+ * 2. 遍历上传目录中的文件
+ * 3. 数据对比,找出存在目录中的文件&不在数据库中的文件
+ * 4. 页面上显示查找出来的文件
+ */
+ public function unuse()
+ {
+ $filesInUse = $this->getAllFilesInUse(); //数据库中在使用的文件
+ $rootPath = app()->getRootPath();
+ $uploadPath = $rootPath . 'storage';
+ $uploadedFiles = getAllFilesByPath($uploadPath, $rootPath); //磁盘上上传的文件
+ $files = MFile::getAll();
+ $dbUploadedFiles = []; //数据库中上传的文件
+ foreach($files as $file){
+ $src = trim($file['src']);
+ if(!empty($src)){
+ $key = getKeyByPath($src);
+ $dbUploadedFiles[$key] = $file;
+ }
+ }
+
+ $uploadedNotInUseFiles = array_diff_key($uploadedFiles, $filesInUse); //磁盘上上传未使用的文件
+ $dbUploadedNotInUseFiles = array_diff_key($dbUploadedFiles, $filesInUse); //数据库中上传未使用的文件
+ $bothNotInUseFiles = array_intersect_key($uploadedNotInUseFiles, $dbUploadedNotInUseFiles); //磁盘和数据库中,两边都未使用
+ $this->data['uploadedNotInUseFiles'] = $uploadedNotInUseFiles;
+ $this->data['dbUploadedNotInUseFiles'] = $dbUploadedNotInUseFiles;
+ $this->data['bothNotInUseFilesKey'] = array_keys($bothNotInUseFiles);
+ return $this->view();
+ }
+
+ //获取所有在使用的文件
+ private function getAllFilesInUse()
+ {
+ $files = [];
+ $blockFiles = Block::getFilesInUse();
+ if(!empty($blockFiles)){
+ $files = array_merge($files, $blockFiles);
+ }
+ $slideFiles = Slide::getFilesInUse();
+ if(!empty($slideFiles)){
+ $files = array_merge($files, $slideFiles);
+ }
+ $linkFiles = Link::getFilesInUse();
+ if(!empty($linkFiles)){
+ $files = array_merge($files, $linkFiles);
+ }
+ $categoryFiles = Category::getFilesInUse();
+ if(!empty($categoryFiles)){
+ $files = array_merge($files, $categoryFiles);
+ }
+ $articleFiles = Archives::getFilesInUse();
+ if(!empty($articleFiles)){
+ $files = array_merge($files, $articleFiles);
+ }
+ return $files;
+ }
+
+
+ //ajax获取文件列表
+ public function list()
+ {
+ if($this->request->isAjax()){
+ $page = input('param.page/d', 1);
+ $size = input('param.size/d', 20);
+ if(!is_integer($page) || $page < 1){
+ $page = 1;
+ }
+ if (!is_integer($size) || $size < 1) {
+ $size = 20;
+ }
+ $type = input('param.type', '');
+ if(!in_array($type, array_keys(MFile::getTypes()))){
+ $type = '';
+ }
+ $items = MFile::getList($type, $page, $size);
+ return $this->json(0, 'ok', $items);
+ }
+ return $this->json(1, '无此操作');
+ }
+ //列表
+ public function index()
+ {
+ $items = MFile::getListPage();
+ $this->data['items'] = $items;
+ $this->data['types'] = MFile::getTypes();
+ return $this->view();
+ }
+}
diff --git a/app/controller/manager/Index.php b/app/controller/manager/Index.php
new file mode 100644
index 0000000..8366805
--- /dev/null
+++ b/app/controller/manager/Index.php
@@ -0,0 +1,83 @@
+data['user'] = Member::findById($auth['user_id'] ?? 0, ['id', 'username', 'nickname', 'mobile']);
+ return $this->view();
+ }
+
+ /**
+ * 控制台
+ *
+ * @return View
+ * @throws Exception
+ */
+ public function dashboard(): View
+ {
+ return $this->view();
+ }
+
+
+ /**
+ * 菜单初始化
+ *
+ * @return Json
+ */
+ public function init(): Json
+ {
+ $res = [];
+ $res['homeInfo'] = ['title' => '控制台', 'href' => "manager/index/dashboard"];
+ $res['logoInfo'] = ['title' => 'CMS管理后台', 'href' => "", 'image' => '/static/manager/image/logo1.png'];
+
+ $menus = CmsRepository::getInstance()->getMenuList(Menu::TYPE_MENU, Menu::SHOW_YES)->toArray();
+ $userId = $this->auth['user_id'] ?? 0;
+ $menus = CmsRepository::getInstance()->handMenuRule($userId, $menus);
+ $menus = CmsRepository::getInstance()->buildMenuChild(0, $menus, 'child');
+ $res['menuInfo'] = $menus;
+
+ return json($res);
+ }
+
+ /**
+ * 缓存清理
+ *
+ * @return Json
+ */
+ public function clear(): Json
+ {
+ $res = ['code' => 1, 'msg' => '服务端清理缓存成功'];
+ sleep(2);
+
+ return json($res);
+ }
+}
diff --git a/app/controller/manager/Link.php b/app/controller/manager/Link.php
new file mode 100644
index 0000000..101502f
--- /dev/null
+++ b/app/controller/manager/Link.php
@@ -0,0 +1,134 @@
+request->isPost()) {
+ $ids = input('post.ids/a');
+ if(empty($ids) || !is_array($ids)) {
+ return $this->json(2, '参数错误,请核对之后再操作!');
+ }
+ $items = MLink::getListByIds($ids);
+ if(!empty($items)){
+ $delIds = [];
+ foreach($items as $item){
+ $delIds[] = $item['id'];
+ }
+ MLink::destroy($delIds);
+ Log::write('link', 'del', '删除了友情链接,涉及到的ID为:' . implode(',', $delIds));
+ return $this->json();
+ }else{
+ return $this->json(3, '待删除友情链接为空');
+ }
+ }
+ return $this->json(1, '非法请求!');
+ }
+
+ //排序
+ public function sort()
+ {
+ Cache::delete('links');//删除缓存
+ if($this->request->isPost()){
+ $id = input('post.id/d');
+ $sort = input('post.sort/d');
+
+ $item = MLink::findById($id);
+ if(empty($item)){
+ return $this->json(3, '该友情链接信息不存在!');
+ }
+ $item->sort = $sort;
+ $item->save();
+ return $this->json();
+
+ }
+ return $this->json(1, '非法请求!');
+ }
+
+ //编辑
+ public function edit()
+ {
+ Cache::delete('links');//删除缓存
+ if($this->request->isPost()){
+ $item = input('post.item/a');
+ $id = input('post.id/d');
+ $img = input('post.img');
+ if(is_numeric($id) && $id > 0) {
+ $link = MLink::findById($id);
+ if(empty($link)) {
+ return $this->json(2, '该友情链接信息不存在!');
+ }
+ if(!empty($img)){
+ $item['src'] = $img;
+ }
+ try {
+ validate(VLink::class)->check($item);
+ $link->save( $item);
+ Log::write('link', 'edit', "友情链接编辑,ID:{$id} ,标题:{$item['title']}");
+ return $this->json();
+ } catch (ValidateException $e) {
+ return $this->json(3, $e->getError());
+ }
+ }
+ return $this->json(1, '参数错误,请核对之后再操作!');
+ } else {
+ $id = input('param.id/d');
+ $item = MLink::getById($id);
+ $imgSize = System::getLinkImageSize();
+
+ $this->data['item'] = $item;
+ $this->data['img_size'] = $imgSize;
+ return $this->view();
+ }
+ }
+
+ //添加
+ public function add()
+ {
+ Cache::delete('links');//删除缓存
+ if($this->request->isPost()){
+ $item = input('post.item/a');
+ $img = input('post.img');
+
+ if(!empty($img)){
+ $item['src'] = $img;
+ }
+ try {
+ validate(VLink::class)->check($item);
+ $link = MLink::create($item);
+ Log::write('link', 'add', "友情链接新增,ID:{$link->id} ,标题:{$item['title']}");
+ return $this->json();
+ } catch (ValidateException $e) {
+ return $this->json(2, $e->getError());
+ }
+ } else {
+ $imgSize = System::getLinkImageSize();
+ $this->data['img_size'] = $imgSize;
+ return $this->view();
+ }
+ }
+
+ public function index()
+ {
+ if ($this->request->isPost()) {
+ $page = $this->request->param('page/d', 1);
+ $size = $this->request->param('size/d', 30);
+
+ $whereMap = [];
+ $orders = ['sort'=>'asc'];
+
+ $list = MLink::findList($whereMap, [], $page, $size, null, $orders);
+
+ return $this->json(0, 'success', $list);
+ }
+ return $this->view();
+ }
+}
diff --git a/app/controller/manager/Log.php b/app/controller/manager/Log.php
new file mode 100644
index 0000000..ed794c5
--- /dev/null
+++ b/app/controller/manager/Log.php
@@ -0,0 +1,56 @@
+request->isPost()) {
+ $page = input('page/d', 1);
+ $limit = input('size/d', 20);
+ $searchParams = input('searchParams');
+ $search = [];
+ if ($searchParams) {
+ foreach ($searchParams as $key => $param) {
+ if ($param) {
+ if ($key == 'begin_time') {
+ $begin = strtotime($param.' 00:00:00');
+ $search[] = ['create_time', '>', $begin];
+ } elseif ($key == 'end_time') {
+ $end = strtotime($param.' 23:59:59');
+ $search[] = ['create_time', '<', $end];
+ } else {
+ $search[] = [$key, 'like', '%'.$param.'%'];
+ }
+ }
+ }
+ }
+
+ $items = LogModel::findList($search, [], $page, $limit, function ($q) {
+ return $q->with(['memberName'])->order('create_time', 'desc');
+ });
+
+ return $this->json(0, '操作成功', $items);
+ }
+
+ return $this->view();
+ }
+}
\ No newline at end of file
diff --git a/app/controller/manager/Login.php b/app/controller/manager/Login.php
new file mode 100644
index 0000000..7c54522
--- /dev/null
+++ b/app/controller/manager/Login.php
@@ -0,0 +1,76 @@
+isPost()) {
+ $param = input('post.data');
+ $username = trim($param['username']);
+ $password = trim($param['password']);
+ $captcha = trim($param['captcha'] ?? '');
+ if (!captcha_check($captcha)) {
+ return $this->json(4001, '验证码错误'.$captcha);
+ }
+
+ if (empty($username) || empty($password)) {
+ return $this->json(4001, '用户名和密码不能为空');
+ }
+ $member = Member::getByUserName($username);
+ if (empty($member)) {
+ return $this->json(4002, '用户名或密码错误');
+ }
+ if ($member['password'] != md5($password.$username)) {
+ return $this->json(4003, '用户名或密码错误');
+ }
+ if ($member['status'] != Member::STATUS_NORMAL) {
+ return $this->json(4004, '账号已被禁用');
+ }
+
+ $userInfo = [
+ 'user_id' => $member['id'],
+ 'username' => $member['username'],
+ 'nickname' => $member['nickname'],
+ 'account_id' => $member['account_id'],//绑定的前台用户ID
+ ];
+
+ $jwtToken = Jwt::generate($userInfo, env('app.expire', 7200));
+
+ $userInfo['token'] = $jwtToken;//jwt生成token
+
+ //记录最后登陆时间
+ $ip = request()->ip();
+ $time = time();
+ Member::updateById($member['id'], [
+ 'login_time' => $time,
+ 'login_ip' => $ip
+ ]);
+ LoginLog::create([
+ 'member_id' => $member['id'],
+ 'name' => $member['username'],
+ 'ip' => $ip,
+ 'create_time' => $time
+ ]);
+ session('auth', $userInfo);
+ return $this->json(0, 'success', ['url' => '/manager']);
+ }
+
+ $viewData = [];
+ return view()->assign($viewData);
+ }
+}
diff --git a/app/controller/manager/Logout.php b/app/controller/manager/Logout.php
new file mode 100644
index 0000000..dd4f8ea
--- /dev/null
+++ b/app/controller/manager/Logout.php
@@ -0,0 +1,21 @@
+request->isPost()) {
+ $ids = input('post.ids/a', []);
+ if (empty($ids)) {
+ $ids[] = input('post.id/d');
+ }
+ MemberModel::deleteByIds($ids);
+ foreach ($ids as $id) {
+ Enforcer::deleteRolesForUser($id);
+ }
+ Log::write(get_class().'Del', 'del', '涉及到的ID为:'.implode(',', $ids));
+ return $this->json();
+ }
+ return $this->json(4001, '非法请求!');
+ }
+
+ /**
+ * 个人详情
+ *
+ * @return Json|View|Redirect
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ * @throws Exception
+ */
+ public function profile()
+ {
+ $id = $this->auth['user_id'] ?? 0;
+
+ if (!$item = MemberModel::findById($id)) {
+ if ($this->request->isAjax()) {
+ return $this->json(4001, '记录不存在');
+ }
+ return $this->error('记录不存在');
+ }
+
+ if ($this->request->isPost()) {
+ $post = input('post.');
+
+ $validate = $this->validateByApi($post, [
+ 'mobile|手机号' => 'require|unique:member,mobile,'.$id,
+ 'nickname|昵称' => 'require|chsAlphaNum|min:2|max:10',
+ 'remark|备注信息' => 'max:255',
+ ]);
+
+ if ($validate !== true) {
+ return $validate;
+ }
+
+ if (!checkMobile($post['mobile'])) {
+ return $this->json(4002, '请输入正确的手机号码');
+ }
+
+ try {
+ $item->save($post);
+ return $this->json();
+ } catch (ValidateException $e) {
+ return $this->json(4001, $e->getError());
+ }
+ }
+
+ $this->data['item'] = $item;
+
+ return $this->view();
+ }
+
+ /**
+ * 编辑
+ *
+ * @return Json|View
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ * @throws Exception
+ */
+ public function edit()
+ {
+ $id = input('id/d', 0);
+
+ if (!$info = MemberModel::findById($id)) {
+ return $this->json(4001, '记录不存在');
+ }
+
+ if ($this->request->isPost()) {
+ $item = input('post.');
+
+ $validate = $this->validateByApi($item, [
+ 'mobile|手机号' => 'require|unique:member,mobile,'.$id,
+ 'nickname|昵称' => 'require|chsAlphaNum|min:2|max:10',
+ 'remark|备注信息' => 'max:255',
+ ]);
+
+ if ($validate !== true) {
+ return $validate;
+ }
+
+ if (!checkMobile($item['mobile'])) {
+ return $this->json(4002, '请输入正确的手机号码');
+ }
+
+ $roles = [];
+ if ($item['roles']) {
+ $roles = $item['roles'];
+ $item['roles'] = implode(',', $item['roles']);
+ }
+
+ Db::startTrans();
+ try {
+ $info->save($item);
+ //删除所有角色
+ Enforcer::deleteRolesForUser($id);
+ //新增角色
+ foreach ($roles as $role) {
+ Enforcer::addRoleForUser($id, $role);
+ }
+ Db::commit();
+ return $this->json();
+ } catch (ValidateException $e) {
+ Db::rollback();
+ return $this->json(4001, $e->getError());
+ }
+ }
+
+ $this->data['item'] = $info;
+ $this->data['roleJson'] = $this->roleJson(explode(',', $info['roles']));
+
+ return $this->view();
+ }
+
+ /**
+ * 单个字段编辑
+ *
+ * @return Json
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ * @throws Exception
+ */
+ public function modify(): Json
+ {
+ if ($this->request->isPost()) {
+ $item = input('post.');
+ $validate = $this->validateByApi($item, [
+ 'field' => 'require',
+ 'value' => 'require',
+ ]);
+
+ if ($validate !== true) {
+ return $validate;
+ }
+
+ if (!$info = MemberModel::findById($item['id'])) {
+ return $this->json(4001, '记录不存在');
+ }
+
+ $update = [$item['field'] => $item['value']];
+
+ try {
+ $info->save($update);
+ return $this->json();
+ } catch (ValidateException $e) {
+ return $this->json(4001, $e->getError());
+ }
+ }
+ return $this->json(4000, '非法请求');
+ }
+
+ /**
+ * 添加
+ *
+ * @return Json|View
+ * @throws Exception
+ */
+ public function add()
+ {
+ if ($this->request->isPost()) {
+ $item = input('post.');
+
+ $validate = $this->validateByApi($item, [
+ 'username|用户名' => 'require|alphaDash|min:4|max:16|unique:member',
+ 'mobile|手机号' => 'require|unique:member',
+ 'nickname|昵称' => 'require|chsAlphaNum|min:2|max:10',
+ 'password|密码' => 'require|min:4|max:16',
+ 'remark|备注信息' => 'max:255',
+ ]);
+
+ if ($validate !== true) {
+ return $validate;
+ }
+
+ if (!checkMobile($item['mobile'])) {
+ return $this->json(4002, '请输入正确的手机号码');
+ }
+
+ $roles = [];
+ if ($item['roles']) {
+ $roles = $item['roles'];
+ $item['roles'] = implode(',', $item['roles']);
+ }
+
+ Db::startTrans();
+ try {
+ $item['password'] = md5($item['password'].$item['username']);
+ $member = MemberModel::create($item);
+ foreach ($roles as $role) {
+ Enforcer::addRoleForUser($member['id'], $role);
+ }
+ Db::commit();
+ return $this->json();
+ } catch (ValidateException $e) {
+ Db::rollback();
+ return $this->json(4001, $e->getError());
+ }
+ }
+
+ $this->data['roleJson'] = $this->roleJson();
+ return $this->view();
+ }
+
+ /**
+ * 修改密码
+ *
+ * @return Json|View|Redirect
+ * @throws Exception
+ */
+ public function password()
+ {
+ $id = input('id/d', 0);
+
+ if (!$item = MemberModel::findById($id)) {
+ if ($this->request->isAjax()) {
+ return $this->json(4001, '记录不存在');
+ }
+ return $this->error('记录不存在');
+ }
+
+ if ($this->request->isPost()) {
+ $post = input('post.');
+ $validate = $this->validateByApi($post, [
+ 'password|密码' => 'require|confirm',
+ ]);
+
+ if ($validate !== true) {
+ return $validate;
+ }
+
+ $password = md5($post['password'].$item['username']);
+
+ try {
+ $item->save(['password' => $password]);
+ return $this->json();
+ } catch (ValidateException $e) {
+ return $this->json(4001, $e->getError());
+ }
+ }
+
+ $this->data['item'] = $item;
+
+ return $this->view();
+ }
+
+ /**
+ * 个人修改密码
+ *
+ * @return Json|View
+ * @throws Exception
+ */
+ public function myPassword()
+ {
+ $id = $this->auth['user_id'] ?? 0;
+ if (!$item = MemberModel::findById($id)) {
+ return $this->json(4001, '记录不存在');
+ }
+
+ if ($this->request->isPost()) {
+ $post = input('post.');
+ $validate = $this->validateByApi($post, [
+ 'old-password|旧密码' => 'require',
+ 'password|密码' => 'require|confirm',
+ ]);
+
+ if ($validate !== true) {
+ return $validate;
+ }
+
+ if ($item['password'] !== md5($post['old-password'].$item['username'])) {
+ return $this->json(4002, '原始密码错误');
+ }
+
+ $password = md5($post['password'].$item['username']);
+
+ try {
+ $item->save(['password' => $password]);
+ return $this->json();
+ } catch (ValidateException $e) {
+ return $this->json(4001, $e->getError());
+ }
+ }
+
+ $this->data['item'] = $item;
+ return $this->view();
+ }
+
+ /**
+ * 列表
+ *
+ * @return View|Json
+ * @throws Exception
+ */
+ public function index()
+ {
+ if ($this->request->isPost()) {
+ $page = input('page/d', 1);
+ $limit = input('size/d', 20);
+ $searchParams = input('searchParams');
+ $where = [];
+ if ($searchParams) {
+ foreach ($searchParams as $key => $param) {
+ if (!empty($param)) {
+ $where[] = [$key, 'like', '%'.$param.'%'];
+ }
+ }
+ }
+
+ $items = MemberModel::findList($where, [], $page, $limit, function ($q) {
+ return $q->order('id', 'desc');
+ });
+
+ return $this->json(0, '操作成功', $items);
+ }
+ return $this->view();
+ }
+
+ /**
+ * 构造角色json数据
+ *
+ * @param array $selected
+ * @return false|string
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ */
+ private function roleJson(array $selected = [])
+ {
+ $roles = RoleModel::where('status', RoleModel::STATUS_NORMAL)
+ ->order('sort', 'desc')
+ ->select()
+ ->toArray();
+ foreach ($roles as $k => $m) {
+ $roles[$k]['checked'] = in_array($m['id'], $selected);
+ $roles[$k]['spread'] = true;
+ }
+ return json_encode($roles, JSON_UNESCAPED_UNICODE);
+ }
+}
\ No newline at end of file
diff --git a/app/controller/manager/Menu.php b/app/controller/manager/Menu.php
new file mode 100644
index 0000000..c5bf342
--- /dev/null
+++ b/app/controller/manager/Menu.php
@@ -0,0 +1,259 @@
+request->isPost()) {
+ $ids = input('post.ids/a', []);
+ if (empty($ids)) {
+ $ids[] = input('post.id/d');
+ }
+ $repo = CmsRepository::getInstance();
+ $repo->delMenuByIds($ids);
+ return $this->json();
+ }
+ return $this->json(4001, '非法请求!');
+ }
+
+ /**
+ * 规则
+ *
+ * @return string[]
+ */
+ private function rule(): array
+ {
+ return [
+ 'pid|父级菜单' => 'require|number',
+ 'title|标题' => 'require|max:100',
+ 'name|路由标识' => 'require',
+ 'remark|备注信息' => 'max:255',
+ ];
+ }
+
+
+ /**
+ * 编辑
+ *
+ * @return Json|View
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ * @throws Exception
+ */
+ public function edit()
+ {
+ $id = input('id/d', 0);
+
+ if (!$info = MenuModel::findById($id)) {
+ return $this->json(4001, '记录不存在');
+ }
+
+ if ($this->request->isPost()) {
+ $item = input('post.');
+ $validate = $this->validateByApi($item, $this->rule());
+ if ($validate !== true) {
+ return $validate;
+ }
+
+ try {
+ $oldPath = $info['path'];
+ $item['path'] = MenuModel::getPath($item['pid']);
+ $info->save($item);
+
+ //刷新所有路径
+ $oldPath = $oldPath.','.$id;
+ $newPath = $item['path'].','.$id;
+ if ($oldPath != $newPath) {
+ MenuModel::refreshPath();
+ }
+ return $this->json();
+ } catch (ValidateException $e) {
+ return $this->json(4001, $e->getError());
+ }
+ }
+
+ $disabled = MenuModel::getAllChildrenIds($id);
+ $disabled[] = $id;
+ $this->data['menuList'] = $this->menuJson([$info['pid']], $disabled);
+ $this->data['item'] = $info;
+
+ return $this->view();
+ }
+
+ /**
+ * 单个字段编辑
+ *
+ * @return Json
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ */
+ public function modify(): Json
+ {
+ if ($this->request->isPost()) {
+ $item = input('post.');
+ $validate = new MenuValidate();
+ if (!$validate->scene('menu_modify')->check($item)) {
+ return $this->json(4002, $validate->getError());
+ }
+
+ if (!$info = MenuModel::findById($item['id'])) {
+ return $this->json(4001, '记录不存在');
+ }
+
+ $update = [$item['field'] => $item['value']];
+
+ try {
+ $info->save($update);
+ return $this->json();
+ } catch (ValidateException $e) {
+ return $this->json(4001, $e->getError());
+ }
+ }
+ return $this->json(4000, '非法请求');
+ }
+
+ /**
+ * 添加
+ *
+ * @return Json|View
+ * @throws Exception
+ */
+ public function add()
+ {
+ $id = input('id/d', 0);
+
+ if ($this->request->isPost()) {
+ $item = input('post.');
+ $validate = $this->validateByApi($item, $this->rule());
+ if ($validate !== true) {
+ return $validate;
+ }
+ try {
+ $item['path'] = MenuModel::getPath($item['pid']);
+ MenuModel::create($item);
+ return $this->json();
+ } catch (ValidateException $e) {
+ return $this->json(4001, $e->getError());
+ }
+ }
+
+ $selected = $id > 0 ? [$id] : [];
+ $this->data['menuList'] = $this->menuJson($selected);
+
+ return $this->view();
+ }
+
+ /**
+ * 常规权限生成
+ *
+ * @return Json|View
+ * @throws Exception
+ */
+ public function generate()
+ {
+ $id = input('id/d', 0);
+
+ if ($this->request->isPost()) {
+ $id = input('id/d', 0);
+ if (!$item = MenuModel::findById($id)) {
+ return $this->json(4002, '记录不存在');
+ }
+
+ if ($item['type'] != MenuModel::TYPE_MENU) {
+ return $this->json(4003, '仅菜单类型可操作');
+ }
+
+ Db::startTrans();
+ try {
+ //自动生成常规操作
+ MenuModel::generate($id, $item['name'], $item['path']);
+ Db::commit();
+ return $this->json();
+ } catch (ValidateException $e) {
+ Db::rollback();
+ return $this->json(4001, $e->getError());
+ }
+ }
+
+ $selected = $id > 0 ? [$id] : [];
+ $this->data['menuList'] = $this->menuJson($selected);
+
+ return $this->view();
+ }
+
+ /**
+ * 列表
+ *
+ * @return View|Json
+ */
+ public function index()
+ {
+ if ($this->request->isPost()) {
+ $menus = CmsRepository::getInstance()->getMenuList();
+ $menus->each(function ($item) {
+ if ($item['type'] == 'menu') {
+ $item->open = false;
+ }
+ });
+ $res = [
+ 'code' => 0,
+ 'msg' => 'success',
+ 'count' => $menus->count(),
+ 'data' => $menus->toArray(),
+ ];
+ return json($res);
+ }
+ return $this->view();
+ }
+
+ /**
+ * xmSelect插件 json数据
+ *
+ * @param array $selected
+ * @param array $disabled
+ * @return false|string
+ */
+ private function menuJson(array $selected = [], array $disabled = [])
+ {
+ $categoryList[] = ['title' => '顶级菜单', 'id' => 0, 'prefix' => '', 'disabled' => false, 'open' => true, 'selected' => in_array(0, $selected)];
+ $menus = CmsRepository::getInstance()->getMenuList();
+ $menus = $menus->toArray();
+ foreach ($menus as $k => $m) {
+ $menus[$k]['selected'] = in_array($m['id'], $selected);
+ $menus[$k]['disabled'] = in_array($m['id'], $disabled);
+ }
+ $menus = CmsRepository::getInstance()->buildMenuChild(0, $menus);
+ $categoryList = array_merge($categoryList, CmsRepository::getInstance()->handleSelectedList($menus));
+ return json_encode($categoryList, JSON_UNESCAPED_UNICODE);
+ }
+}
\ No newline at end of file
diff --git a/app/controller/manager/Role.php b/app/controller/manager/Role.php
new file mode 100644
index 0000000..f7c84c9
--- /dev/null
+++ b/app/controller/manager/Role.php
@@ -0,0 +1,270 @@
+request->isPost()) {
+ $ids = input('post.ids/a', []);
+ if (empty($ids)) {
+ $ids[] = input('post.id/d');
+ }
+ RoleModel::deleteByIds($ids);
+ Log::write(get_class().'Del', 'del', '涉及到的ID为:'.implode(',', $ids));
+ return $this->json();
+ }
+ return $this->json(4001, '非法请求!');
+ }
+
+ /**
+ * 编辑
+ *
+ * @return Json|View
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ * @throws Exception
+ */
+ public function edit()
+ {
+ $id = input('id/d', 0);
+
+ if (!$info = RoleModel::findById($id)) {
+ return $this->json(4001, '记录不存在');
+ }
+
+ if ($this->request->isPost()) {
+ $item = input('post.');
+ $validate = $this->validateByApi($item, [
+ 'title' => 'require',
+ ]);
+
+ if ($validate !== true) {
+ return $validate;
+ }
+
+ try {
+ $info->save($item);
+ return $this->json();
+ } catch (ValidateException $e) {
+ return $this->json(4001, $e->getError());
+ }
+ }
+
+ $this->data['item'] = $info;
+
+ return $this->view();
+ }
+
+ /**
+ * 单个字段编辑
+ *
+ * @return Json
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ * @throws Exception
+ */
+ public function modify(): Json
+ {
+ if ($this->request->isPost()) {
+ $item = input('post.');
+ $validate = $this->validateByApi($item, [
+ 'field' => 'require',
+ 'value' => 'require',
+ ]);
+
+ if ($validate !== true) {
+ return $validate;
+ }
+
+ if (!$info = RoleModel::findById($item['id'])) {
+ return $this->json(4001, '记录不存在');
+ }
+
+ $update = [$item['field'] => $item['value']];
+
+ try {
+ $info->save($update);
+ return $this->json();
+ } catch (ValidateException $e) {
+ return $this->json(4001, $e->getError());
+ }
+ }
+ return $this->json(4000, '非法请求');
+ }
+
+ /**
+ * 添加
+ *
+ * @return Json|View
+ * @throws Exception
+ */
+ public function add()
+ {
+ if ($this->request->isPost()) {
+ $item = input('post.');
+
+ $validate = $this->validateByApi($item, [
+ 'title' => 'require',
+ ]);
+
+ if ($validate !== true) {
+ return $validate;
+ }
+
+ try {
+ RoleModel::create($item);
+ return $this->json();
+ } catch (ValidateException $e) {
+ return $this->json(4001, $e->getError());
+ }
+ }
+
+ return $this->view();
+ }
+
+ /**
+ * 角色权限
+ *
+ * @return Json|View
+ * @throws Exception
+ */
+ public function rule()
+ {
+ $id = input('id/d', 0);
+
+ if (!$item = RoleModel::findById($id)) {
+ return $this->json(4001, '记录不存在');
+ }
+
+ if ($this->request->isPost()) {
+ $ids = input('post.ids');
+ $roleUpdate = $ids;//角色更新数据
+ $ids = explode(',', $ids);
+
+ Db::startTrans();
+ try {
+ //查询角色已有权限
+ $hasRules = Rules::where('ptype', 'p')->where('v0', $id)->select()->toArray();
+ //角色最新权限列表
+ $currentRules = MenuModel::where('id', 'in', $ids)->field('name')->select()->toArray();
+
+ foreach ($currentRules as &$rule) {
+ $route = explode(':', $rule['name']);
+ $v1 = $route[0];
+ $v2 = $route[1] ?? 'index';
+
+ $rule['ptype'] = 'p';
+ $rule['v0'] = $id;
+ $rule['v1'] = $v1;
+ $rule['v2'] = $v2;
+ }
+
+ foreach ($hasRules as $k => $has) {
+ foreach ($currentRules as $m => $current) {
+ if ($has['ptype'] == $current['ptype'] && $has['v0'] == $current['v0'] && $has['v1'] == $current['v1'] && $has['v2'] == $current['v2']) {
+ unset($currentRules[$m]);//删除当前权限列表已存在的 currentRules剩下的就是需要添加的记录
+ unset($hasRules[$k]);//删除已有权限中存在的 hasRules剩下的就是需要删除的记录
+ }
+ }
+ }
+
+ $insert = $currentRules;//需要添加的数据
+ $delete = $hasRules;//需要删除的数据
+
+ $deleteIds = array_column($delete, 'id');//需要删除的ID
+ (new Rules())->saveAll($insert);
+ (new Rules())->where('id', 'in', $deleteIds)->delete();
+ cache('tauthz', null);//权限缓存清空
+
+ $item->save(['rules' => $roleUpdate]);
+ Db::commit();
+ return $this->json();
+ } catch (ValidateException $e) {
+ Db::rollback();
+ return $this->json(4001, $e->getError());
+ }
+ }
+
+ $selected = explode(',', $item['rules']);
+
+ $this->data['authJson'] = $this->authJson($selected);
+ $this->data['item'] = $item;
+
+ return $this->view();
+ }
+
+ /**
+ * 构造json数据
+ *
+ * @param array $selected
+ * @return false|string
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ */
+ private function authJson(array $selected = [])
+ {
+ $menus = Menu::field("id,pid,title,sort")
+ ->where('status', Menu::STATUS_NORMAL)
+ ->order('sort', 'desc')
+ ->order('id', 'asc')
+ ->select()->toArray();
+ foreach ($menus as $k => $m) {
+ $menus[$k]['checked'] = in_array($m['id'], $selected);
+ $menus[$k]['open'] = true;
+ }
+ $menus = CmsRepository::getInstance()->buildMenuChild(0, $menus);
+ return json_encode($menus, JSON_UNESCAPED_UNICODE);
+ }
+
+ /**
+ * 列表
+ *
+ * @return View|Json
+ * @throws Exception
+ */
+ public function index()
+ {
+ if ($this->request->isPost()) {
+ $page = input('page/d', 1);
+ $limit = input('size/d', 20);
+ $items = RoleModel::findList([], [], $page, $limit, function ($q) {
+ return $q->order('sort', 'desc')->order('id', 'asc');
+ });
+
+ return $this->json(0, '操作成功', $items);
+ }
+ return $this->view();
+ }
+}
\ No newline at end of file
diff --git a/app/controller/manager/Rule.php b/app/controller/manager/Rule.php
new file mode 100644
index 0000000..6a83a80
--- /dev/null
+++ b/app/controller/manager/Rule.php
@@ -0,0 +1,183 @@
+request->isAjax()) {
+ $id = input('post.id');
+ $sort = input('post.sort');
+ $num = input('post.num/d', 1);
+ if($num <= 0){
+ $num = 1;
+ }
+ if(!in_array($sort, ['up', 'down'], true)){
+ return $this->json(2, '参数错误');
+ }
+ $item = AuthRule::getById($id);
+ if(empty($item)){
+ return $this->json(3, '权限不存在');
+ }
+ if($sort == 'up'){
+ $where = "parent_id = {$item['parent_id']} and sort < {$item['sort']}";
+ $order = "sort desc";
+ }else{
+ $where = "parent_id = {$item['parent_id']} and sort > {$item['sort']}";
+ $order = "sort asc";
+ }
+ $forSortItems = AuthRule::getListByWhereAndOrder($where, $order, $num);
+ if(!empty($forSortItems)){
+ $updateData = [];
+ $forSortCount = count($forSortItems);
+ for($i = 0; $i < $forSortCount; $i++){
+ if($i == 0){
+ $updateData[] = [
+ 'id' => $forSortItems[$i]['id'],
+ 'sort' => $item['sort']
+ ];
+ }else{
+ $updateData[] = [
+ 'id' => $forSortItems[$i]['id'],
+ 'sort' => $forSortItems[$i - 1]['sort']
+ ];
+ }
+ }
+ $updateData[] = [
+ 'id' => $item['id'],
+ 'sort' => $forSortItems[$i - 1]['sort']
+ ];
+ if(!empty($updateData)){
+ $model = new AuthRule();
+ $model->saveAll($updateData);
+ $sortStr = $sort == 'up' ? '上移' : '下调';
+ Log::write('rule', 'sort', "权限排序,ID:{$id} ,标题:{$item['title']},{$sortStr}了{$num}位");
+ return $this->json();
+ }
+ }
+ return $this->json(4, '无须调整排序!');
+ }
+ return $this->json(1, '非法请求!');
+ }
+
+ /**
+ * 权限删除
+ */
+ public function del()
+ {
+ if ($this->request->isAjax()) {
+ $ids = input('post.ids/a');
+ $items = AuthRule::where('id', 'in', $ids)->select();
+ if(!$items){
+ return $this->json(1, '无此权限');
+ }
+ if(AuthRule::where('parent_id', 'in', $ids)->count()){
+ return $this->json(2, '当前权限有下级权限,不可删除');
+ }
+ AuthRule::destroy($ids);
+ AuthGroup::resetGroupRulesCache();
+ $ids = implode(',', $ids);
+ Log::write('rule', 'del', "权限删除,ID:{$ids}");
+ return $this->json();
+ }
+ return $this->json(1, '非法请求!');
+ }
+
+ /**
+ * 权限修改
+ */
+ public function edit()
+ {
+ if($this->request->isPost()){
+ $item = input('post.item/a');
+ $id = input('post.id');
+ $rule = AuthRule::getById($id);
+ if(empty($rule)){
+ return $this->json(1, '请选择正确的权限');
+ }
+ $rule2 = AuthRule::getByName($item['name']);
+ if(!empty($rule2) && $rule2['id'] != $id){
+ return $this->json(2, '已存在相同权限['.$item['name'].']');
+ }
+ try {
+ validate(VAuthRule::class)->check($item);
+ AuthRule::updateById($id, $item);
+ AuthGroup::resetGroupRulesCache();
+ Log::write('rule', 'edit', "权限编辑,ID:{$id}, 标题:{$item['title']}");
+ return $this->json();
+ } catch (ValidateException $e) {
+ return $this->json(3, $e->getError());
+ }
+ }
+ $id = input('param.id/d');
+ $rule = AuthRule::getById($id);
+ if(empty($rule)){
+ return $this->json(1,'无此权限信息,请核对之后再操作!');
+ }else{
+ $this->data['item'] = $rule;
+ if($rule['parent_id'] > 0){
+ $parent = AuthRule::getById($rule['parent_id']);
+ $this->data['parent'] = $parent;
+ }
+ return $this->view();
+ }
+ }
+
+ /**
+ * 权限添加
+ */
+ public function add()
+ {
+ if($this->request->isPost()){
+ $item = input('post.item/a');
+ try {
+ validate(VAuthRule::class)->check($item);
+ $rule = AuthRule::getByName($item['name']);
+ if(!empty($rule)){
+ return $this->json(1, '已存在相同权限');
+ }
+ $rule = AuthRule::create($item);
+ //基本权限的话需要重置所有已有角色权限缓存
+ if ($item['is_base'] > 0) {
+ AuthGroup::resetGroupRulesCache();
+ } else {
+ AuthGroup::resetGroupRulesCache(1);
+ }
+ Log::write('rule', 'add', "权限新增,ID:{$rule->id}, 标题:{$item['title']}");
+ return $this->json();
+ } catch (ValidateException $e) {
+ return $this->json(2, $e->getError());
+ }
+ }
+ $parentId = input('param.parent_id/d',0);
+ if($parentId > 0){
+ $parent = AuthRule::getById($parentId);
+ $this->data['parent'] = $parent;
+ }
+ $this->data['parentId'] = $parentId;
+ return $this->view();
+ }
+
+ /**
+ * 权限列表(全部)
+ */
+ public function index()
+ {
+ $list = AuthRule::getListTree();
+ $this->data['items'] = $list;
+ return $this->view();
+ }
+}
\ No newline at end of file
diff --git a/app/controller/manager/Slide.php b/app/controller/manager/Slide.php
new file mode 100644
index 0000000..9f20e17
--- /dev/null
+++ b/app/controller/manager/Slide.php
@@ -0,0 +1,277 @@
+request->param('id/d', 0);
+
+ if (!$slide = OperationRepository::getInstance()->findSlideById($id)) {
+ return $this->json(4001, '数据不存在');
+ }
+
+ if ($this->request->isPost()) {
+ $item = input('post.item/a');
+ $validate = new VSlide();
+ if (!$validate->scene('slide')->check($item)) {
+ return $this->json(4002, $validate->getError());
+ }
+
+ unset($item['id']);
+ OperationRepository::getInstance()->updateSlide($item, $id);
+ return $this->json();
+ }
+
+ $this->data['item'] = $slide;
+ $this->data['positionsJson'] = $this->xmSelectPositionsJson([$slide['position']]);
+ $this->data['id'] = $id;
+
+ return $this->view();
+ }
+
+ /**
+ * 添加
+ *
+ * @return View|Json
+ */
+ public function add()
+ {
+ $repo = OperationRepository::getInstance();
+
+ if ($this->request->isPost()) {
+ $item = input('post.item/a');
+ $validate = new VSlide();
+ if (!$validate->scene('slide')->check($item)) {
+ return $this->json(4002, $validate->getError());
+ }
+
+ $item['type'] = $item['type'] ?? 'img';
+ $repo->createSlide($item);
+ return $this->json();
+ }
+
+ $this->data['positionsJson'] = $this->xmSelectPositionsJson();
+ return $this->view();
+ }
+
+ /**
+ * 轮播图列表
+ *
+ * @return Json|View
+ * @throws Exception
+ */
+ public function index()
+ {
+ $repo = OperationRepository::getInstance();
+ if ($this->request->isPost()) {
+ $position = $this->request->param('position/s', '');
+ $page = $this->request->param('page/d', 1);
+ $size = $this->request->param('size/d', 30);
+
+ $whereMap = [];
+ $orders = ['sort'=>'asc'];
+ if (!empty($position)) {
+ $whereMap[] = ['position', '=', $position];
+ }
+
+ $list = $repo->slideList($whereMap, [], $page, $size, null, $orders);
+
+ return $this->json(0, 'success', $list);
+ }
+
+ $this->data['positions'] = $repo->slidePositions();
+ return $this->view();
+ }
+
+ /**
+ * 排序
+ * @return Json
+ */
+ public function sort()
+ {
+ if (!$this->request->isPost()) {
+ return $this->json(4000, '非法请求');
+ }
+ try {
+ $id = $this->request->param('id/d', 0);
+ $sort = $this->request->param('sort/d', 0);
+ OperationRepository::getInstance()->updateSlide(['sort'=>$sort], $id);
+ } catch (Exception $e) {
+ return $this->json(4001, '排序失败');
+ }
+ return $this->json();
+ }
+
+ /**
+ * 删除
+ * @return Json
+ */
+ public function del()
+ {
+ if (!$this->request->isPost()) {
+ return $this->json(4000, '非法请求');
+ }
+
+ $ids = $this->request->param('ids/a', []);
+ if (empty($ids)) {
+ $ids[] = $this->request->param('id/d', 0);
+ $ids = array_filter($ids);
+ }
+
+ if (count($ids)) {
+ OperationRepository::getInstance()->deleteSlides($ids);
+ Log::write(get_class(), 'del', '删除了轮播图,涉及到的ID为:'.implode(',', $ids));
+ }
+ return $this->json();
+ }
+
+ /**
+ * 显示位置下拉选项数据
+ *
+ * @param array $selected
+ * @param array $disabled
+ * @return false|string
+ */
+ private function xmSelectPositionsJson(array $selected = [], array $disabled = [])
+ {
+ $positionList = OperationRepository::getInstance()->slidePositions();
+ foreach ($positionList as $k => $item) {
+ $positionList[$k]['selected'] = in_array($item['key'], $selected);
+ $positionList[$k]['disabled'] = in_array($item['key'], $disabled);
+ }
+ $positionList = CmsRepository::getInstance()->handleSelectedList($positionList);
+ return json_encode($positionList, JSON_UNESCAPED_UNICODE);
+ }
+
+ /**
+ * 轮播图显示位置管理
+ *
+ */
+ public function position()
+ {
+ $repo = OperationRepository::getInstance();
+
+ if ($this->request->isPost()) {
+ $list = $repo->slidePositionList([], [], 1, 0);
+
+ return $this->json(0, 'success', $list);
+ }
+ return $this->view();
+ }
+
+ /**
+ * 添加显示位置信息
+ *
+ * @return Json|View
+ */
+ public function addPosition()
+ {
+ $repo = OperationRepository::getInstance();
+
+ if ($this->request->isPost()) {
+ $item = input('post.item/a');
+ try {
+ $this->validate($item, [
+ 'title|标题' => 'max:250',
+ 'key|位置标识' => 'require|max:100|alphaDash'
+ ]);
+ } catch (ValidateException $e) {
+ return $this->json(4002, $e->getError());
+ }
+
+ if ($repo->slidePositionExists($item['key'])) {
+ return $this->json(4003, '当前位置标识已存在!');
+ }
+
+ $repo->createSlidePosition($item);
+ return $this->json();
+ }
+
+ return $this->view();
+ }
+
+ /**
+ * 编辑显示位置信息
+ *
+ * @return Json|View
+ * @throws DbException
+ * @throws ModelNotFoundException
+ * @throws DataNotFoundException
+ */
+ public function editPosition()
+ {
+ $id = $this->request->param('id/d', 0);
+
+ if (!$position = OperationRepository::getInstance()->findSlidePositionById($id)) {
+ return $this->json(4001, '数据不存在');
+ }
+
+ if ($this->request->isPost()) {
+ $item = input('post.item/a');
+ try {
+ $this->validate($item, [
+ 'title|标题' => 'max:250'
+ ]);
+ } catch (ValidateException $e) {
+ return $this->json(4002, $e->getError());
+ }
+
+ unset($item['id']);
+ unset($item['key']);
+ OperationRepository::getInstance()->updateSlidePosition($item, $id);
+ return $this->json();
+ }
+
+ $this->data['item'] = $position;
+ $this->data['id'] = $id;
+
+ return $this->view();
+ }
+
+ /**
+ * 删除显示位置信息
+ * @return Json
+ */
+ public function delPosition()
+ {
+ if (!$this->request->isPost()) {
+ return $this->json(4000, '非法请求');
+ }
+
+ $ids = $this->request->param('ids/a', []);
+ if (empty($ids)) {
+ $ids[] = $this->request->param('id/d', 0);
+ $ids = array_filter($ids);
+ }
+
+ if (count($ids)) {
+ OperationRepository::getInstance()->deleteSlidePositions($ids);
+ Log::write(get_class(), 'delPosition', '删除了轮播显示位置,涉及到的ID为:'.implode(',', $ids));
+ }
+
+ return $this->json();
+ }
+
+
+}
\ No newline at end of file
diff --git a/app/controller/manager/Upload.php b/app/controller/manager/Upload.php
new file mode 100644
index 0000000..abff79b
--- /dev/null
+++ b/app/controller/manager/Upload.php
@@ -0,0 +1,239 @@
+isCompress = $system['compress'] ?? true;
+ }
+ $this->validate = new VUpload();
+ $this->uploadPath = Config::get('filesystem.disks.local.url');
+ $this->videoUploadPath = Config::get('filesystem.disks.video.url');
+
+ $uploadDiskPath = public_path().$this->uploadPath;
+ $videoUploadDiskPath = public_path().$this->videoUploadPath;
+ if (!is_dir($uploadDiskPath)) {
+ @mkdir($uploadDiskPath, 0777, true);
+ }
+ if (!is_dir($videoUploadDiskPath)) {
+ @mkdir($videoUploadDiskPath, 0777, true);
+ }
+ if (is_writable($uploadDiskPath)) {
+ $this->uploadPathIsWritable = 1;
+ }
+ if (is_writable($videoUploadDiskPath)) {
+ $this->videoUploadPathIsWritable = 1;
+ }
+
+
+ $this->DIRECTORY_SEPARATOR = DIRECTORY_SEPARATOR == "\\" ? "/" : DIRECTORY_SEPARATOR;
+ ini_set('max_execution_time', '0');
+ ini_set("memory_limit", '-1');
+ set_time_limit(0);
+ }
+
+ //视频上传
+ public function video()
+ {
+ $video = request()->file('video_video');
+ if (!$this->videoUploadPathIsWritable) {
+ return $this->json(1, '上传文件夹需要写入权限');
+ }
+ if ($this->validate->checkVideo($video)) {
+ $md5 = $video->md5();//文件md5
+ if ($fileItem = File::where('md5', $md5)->find()) {
+ $return['src'] = $fileItem['src'];
+ $fileItem['updated_at'] = date('Y-m-d H:i:s');
+ $fileItem->save();
+ return $this->json(200, '该文件已存在 路径为:'.$fileItem['path'], $return);
+ }
+
+ $path = request()->param('path/s', '');//指定路径 基于public下 若为空则默认
+ $hasPath = !empty($path);
+
+ // 去除以/storage/开头的部分 如/storage/20210808/test => 20210808/test
+ $path = ltrim(trim($path, $this->DIRECTORY_SEPARATOR), \app\model\Attachment::ROOT_NAME.$this->DIRECTORY_SEPARATOR);
+ $datePath = $hasPath ? $path : 'videos'.$this->DIRECTORY_SEPARATOR.date('Ym');//自定义目录
+ checkPathExistWithMake(public_path().$this->uploadPath.'/'.$datePath);
+ $src = Filesystem::putFile($datePath, $video, 'uniqid');
+ $src = $this->uploadPath.'/'.$src;
+ $return['src'] = $src;
+ File::add($video, $src, $md5, 'video'); //加入上传文件表
+ return $this->json(0, 'ok', $return);
+ } else {
+
+ $errorMsg = Lang::get($this->validate->getError());
+ return $this->json(1, $errorMsg);
+ }
+ }
+
+ //文件上传(通用)
+ public function file()
+ {
+ $file = request()->file('file_file');
+ $md5 = $file->md5();//文件md5
+ $fileName = $file->getOriginalName();//原始文件名
+ if ($fileItem = File::where('md5', $md5)->find()) {
+ $return['src'] = $fileItem['src'];
+ $return['name'] = $fileName;
+ $fileItem['updated_at'] = date('Y-m-d H:i:s');
+ return $this->json(200, '该文件已存在 路径为:'.$fileItem['path'], $return);
+ }
+ if ($this->validate->checkFile($file)) {
+ try {
+ if (!$this->uploadPathIsWritable) {
+ throw new \Exception('上传文件夹需要写入权限');
+ }
+ $path = request()->param('path/s', '');//指定路径 基于public下 若为空则默认
+ $hasPath = !empty($path);
+ $datePath = $hasPath ? $path : 'files'.$this->DIRECTORY_SEPARATOR.date('Ym');//自定义目录
+ checkPathExistWithMake(public_path().$this->uploadPath.'/'.$datePath);
+ $src = Filesystem::putFile('files'.$this->DIRECTORY_SEPARATOR.date('Ym'), $file, 'uniqid');
+ $src = $this->uploadPath.$this->DIRECTORY_SEPARATOR.$src;
+ $return['src'] = $src;
+ $return['name'] = $fileName;
+ File::add($file, $src, $md5, 'file'); //加入上传文件表
+ } catch (\Exception $e) {
+ return $this->json(1, $e->getMessage());
+ }
+ return $this->json(0, 'ok', $return);
+ } else {
+ $errorMsg = Lang::get($this->validate->getError());
+ return $this->json(1, $errorMsg);
+ }
+ }
+
+ //图片上传(通用)
+ public function image()
+ {
+ // 字段名 image-image避免冲突 layui组件自动生成的隐藏file input框中name容易重名冲突
+ $image = request()->file('image_image');
+
+ // $img = ImageManagerStatic::cache(function($imageObj) use ($image) {
+ // $imageObj->make($image)->resize(300, 200)->greyscale();
+ // }, 10, true);
+ // $img = (string)ImageManagerStatic::make($image)->fit(350, 610)->encode('png');
+ // echo $img;
+ // exit;
+
+ $md5 = $image->md5();//文件md5
+ $path = request()->param('path/s', '');//指定路径 基于public下 若为空则默认
+ $hasPath = !empty($path);
+
+ if ($this->validate->checkImage($image)) {
+ if ($fileItem = File::where('md5', $md5)->find()) {
+ $return['src'] = $fileItem['src'];
+ $fileItem['updated_at'] = date('Y-m-d H:i:s');
+
+ return $this->json(200, '该文件已存在 路径为:'.$fileItem['path'], $return);
+ }
+
+ try {
+ if (!$this->uploadPathIsWritable) {
+ throw new \Exception('上传文件夹需要写入权限');
+ }
+
+ // 去除以/storage/开头的部分 如/storage/20210808/test => 20210808/test
+ if (strpos(trim($path, $this->DIRECTORY_SEPARATOR), \app\model\Attachment::ROOT_NAME.$this->DIRECTORY_SEPARATOR) === 0) {
+ $path = substr(trim($path, $this->DIRECTORY_SEPARATOR), strlen(\app\model\Attachment::ROOT_NAME.$this->DIRECTORY_SEPARATOR));
+ }
+
+ $datePath = $hasPath ? $path : 'images'.$this->DIRECTORY_SEPARATOR.date('Ym');//自定义目录
+ $datePath = ($datePath == $this->DIRECTORY_SEPARATOR."storage") ? $this->DIRECTORY_SEPARATOR : $datePath;
+
+ checkPathExistWithMake(public_path().$this->uploadPath.'/'.$datePath);
+
+ $src = Filesystem::putFile($datePath, $image, 'uniqid');
+
+ $src = $this->uploadPath.$this->DIRECTORY_SEPARATOR.$src;
+ $suffix = strtolower($image->getOriginalExtension());
+
+ $cutJson= input('cut');
+ $cutList = json_decode($cutJson, true);
+ // 裁剪尺寸
+ if (!empty($cutList)) {
+ $srcArr = explode('.', $src);
+ foreach ($cutList as $cut) {
+ ImageManagerStatic::make($image)->fit($cut['width'], $cut['height'])->save(public_path().$srcArr[0].'_'.$cut['name'].'.'.$suffix);
+ }
+ }
+ $return['src'] = $src;
+ File::add($image, $src, $md5); //加入上传文件表
+ } catch (\Exception $e) {
+ return $this->json(1, $e->getMessage());
+ }
+ return $this->json(0, 'ok', $return);
+ } else {
+ $errorMsg = Lang::get($this->validate->getError());
+ return $this->json(1, $errorMsg);
+ }
+ }
+
+ //富文本编辑器商城图片
+ public function wangImage()
+ {
+
+ $imageArr = request()->file('wang_img'); // 该方式,前端js上传方法中字段名称必须以数组形式传参 如 wang_img[] = 值
+ $errno = 0;
+ $data = [];
+
+ if (!$this->uploadPathIsWritable) {
+ $errno = 1;
+ $data[] = '上传文件夹需要写入权限';
+ } else {
+ foreach ($imageArr as $image) {
+ $md5 = $image->md5();//文件md5
+ if ($fileItem = File::where('md5', $md5)->find()) {
+ $return['src'] = $fileItem['src'];
+ $fileItem['updated_at'] = date('Y-m-d H:i:s');
+
+ $data[] = $fileItem['src'];
+ $fileItem->save();
+ continue;
+ }
+
+ if ($this->validate->checkImage($image)) {
+ checkPathExistWithMake(public_path().$this->uploadPath.'/images/'.date('Ym'));
+ $src = Filesystem::putFile('images/'.date('Ym'), $image, 'uniqid');
+ $src = $this->uploadPath.$this->DIRECTORY_SEPARATOR.$src;
+ $data[] = $src;
+ if ($this->isCompress) {
+ Image::resize($src);
+ }
+ File::add($image, $src, $md5); //加入上传文件表
+ } else {
+ $errno = 1;
+ $data = [];
+ $data[] = Lang::get($this->validate->getError());
+ break;
+ }
+ }
+ }
+
+ $return['errno'] = $errno;
+ $return['data'] = $data;
+ return json($return);
+ }
+}
\ No newline at end of file
diff --git a/app/controller/manager/Wechat.php b/app/controller/manager/Wechat.php
new file mode 100644
index 0000000..9ee7a27
--- /dev/null
+++ b/app/controller/manager/Wechat.php
@@ -0,0 +1,27 @@
+view();
+ }
+
+
+
+
+}
\ No newline at end of file
diff --git a/app/event.php b/app/event.php
new file mode 100644
index 0000000..44550ba
--- /dev/null
+++ b/app/event.php
@@ -0,0 +1,16 @@
+ [
+ ],
+
+ 'listen' => [
+ 'AppInit' => [],
+ 'HttpRun' => [],
+ 'HttpEnd' => [],
+ 'LogLevel' => [],
+ 'LogWrite' => [],
+ ],
+
+ 'subscribe' => [],
+];
diff --git a/app/exception/ApiRedirectException.php b/app/exception/ApiRedirectException.php
new file mode 100644
index 0000000..40585eb
--- /dev/null
+++ b/app/exception/ApiRedirectException.php
@@ -0,0 +1,10 @@
+attempts() > 3) {
+// //通过这个方法可以检查这个任务已经重试了几次了
+// echo sprintf('发送短信失败过多');
+// }
+
+
+ //如果任务执行成功后 记得删除任务,不然这个任务会重复执行,直到达到最大重试次数后失败后,执行failed方法
+ $job->delete();
+
+ // 也可以重新发布这个任务
+// $job->release(3); //$delay为延迟时间
+
+ }
+
+ public function failed($data){
+
+ // ...任务达到最大重试次数后,失败了
+ }
+}
\ No newline at end of file
diff --git a/app/middleware.php b/app/middleware.php
new file mode 100644
index 0000000..e588a2d
--- /dev/null
+++ b/app/middleware.php
@@ -0,0 +1,10 @@
+authorization ?? '';
+ if (empty($authorization)) {
+ return json(['code' => 6001, 'msg' => '请填写token']);
+ }
+ if (!JwtService::validate($authorization)) {
+ return json(['code' => 6001, 'msg' => 'token验证失败或已失效']);
+ }
+
+ $userInfo = $request->user ?? [];
+ if (!isset($userInfo['user_id']) || empty($userInfo['user_id'])) {
+ return json(['code' => 6001, 'msg' => 'token已失效']);
+ }
+
+ // 自定义过期时间校验。
+ if(isset($userInfo['expire_time']) && time() >= $userInfo['expire_time']) {
+ return json(['code' => 6001, 'msg' => 'token已失效']);
+ }
+
+ return $next($request);
+ }
+}
\ No newline at end of file
diff --git a/app/middleware/Auth.php b/app/middleware/Auth.php
new file mode 100644
index 0000000..3f72304
--- /dev/null
+++ b/app/middleware/Auth.php
@@ -0,0 +1,50 @@
+controller());
+ $controller = str_replace($module.'.', '', $controller);
+ $controller = str_replace('.', '/', $controller);//兼容多层级目录 如 /manager/test/article/index
+ $action = unCamelize(request()->action());
+ $roles = Enforcer::getRolesForUser($auth['user_id']);
+// $per = Enforcer::getPermissionsForUser($roles[0]);
+// var_dump($controller);
+// var_dump($action);
+// var_dump($roles);
+// var_dump($per);
+// exit;
+// return $next($request);//暂时停用权限校验
+// var_dump($controller);
+// var_dump($action);
+// var_dump(Enforcer::hasPermissionForUser(1, $controller, 'group-make'));exit;
+
+ foreach ($roles as $role) {
+ // TODO 关注批量权限检测是否可用
+ //只需要有一个角色具有权限就放通 此处第一个参数不是用户 而是 角色 此方法是检测用户|角色是否具有某个权限的公用方法
+ if (Enforcer::hasPermissionForUser($role, $controller, $action)) {
+ return $next($request);
+ }
+ }
+
+ if (request()->isAjax()) {
+ return json(['code' => 4001, 'msg' => '没有权限']);
+ } else {
+ return view('/manager/error/jump')->assign('msg', '很抱歉,您还没有权限,请联系管理员开通!');
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/middleware/Csrf.php b/app/middleware/Csrf.php
new file mode 100644
index 0000000..c6bfccf
--- /dev/null
+++ b/app/middleware/Csrf.php
@@ -0,0 +1,56 @@
+isPost()){
+ $check = $request->checkToken();
+ if(false === $check) {
+// return $this->csrfError($request);
+ }
+ }
+ return $next($request);
+ }
+
+ protected function csrfError($request, $msg = '非法请求, 用户身份认证失败!')
+ {
+ if($request->isAjax()) {
+ return json(['code' => 401, 'msg' => $msg], 200);
+ } else {
+ $referer = $_SERVER['HTTP_REFERER'] ?? null;
+ if (empty($referer)) {
+ $url = '/';
+ } else {
+ $domain = $request->domain();
+ $urlInfo = parse_url($referer);
+ $scheme = $urlInfo['scheme'] ?? '';
+ $requestSrc = '';
+ if (!empty($scheme)) {
+ $requestSrc = $scheme.'://'.($urlInfo['host'] ?? '');
+ }
+ if($domain != $requestSrc) {
+ $url = '/';
+ } else {
+ $url = 'javascript:history.back(-1);';
+ }
+ }
+ $errorData = [
+ 'code'=> 401,
+ 'msg' => $msg,
+ 'data' => [],
+ 'wait' => 5,
+ 'url' => $url
+ ];
+ return view('error/400', $errorData);
+ // 返回401视图 response type has html、json、jsonp、xml、file、view、redirect
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/middleware/JWT.php b/app/middleware/JWT.php
new file mode 100644
index 0000000..3ed39b5
--- /dev/null
+++ b/app/middleware/JWT.php
@@ -0,0 +1,37 @@
+header('Authorization');
+ $tokenStr = $request->param('token/s', '');
+
+ if ($authorization) {
+ $authorization = str_replace('Bearer ', '', $authorization);
+ }
+
+ //优先取header中token
+ $token = $authorization ?: $tokenStr;
+ $userInfo = [];
+ if (!empty($token)) {
+ $userInfo = JwtService::parse($token);//token中携带的简易用户信息
+ }
+
+ $request->user = $userInfo;
+ // authorization用于移交ApiLogin认证
+ $request->authorization = $token;
+
+ return $next($request);
+ }
+}
\ No newline at end of file
diff --git a/app/middleware/Log.php b/app/middleware/Log.php
new file mode 100644
index 0000000..4280db8
--- /dev/null
+++ b/app/middleware/Log.php
@@ -0,0 +1,26 @@
+controller(), $request->action(), $request->pathinfo(), $request->method());
+
+ return $response;
+
+ }
+}
\ No newline at end of file
diff --git a/app/middleware/Login.php b/app/middleware/Login.php
new file mode 100644
index 0000000..40080f3
--- /dev/null
+++ b/app/middleware/Login.php
@@ -0,0 +1,25 @@
+url(true);
+ return redirect(url('login/index').'?url='.urlencode($url));
+ }
+
+ return $next($request);
+ }
+}
\ No newline at end of file
diff --git a/app/model/Account.php b/app/model/Account.php
new file mode 100644
index 0000000..70bcee6
--- /dev/null
+++ b/app/model/Account.php
@@ -0,0 +1,11 @@
+ $per,
+ 'query' => $param
+ ];
+ return self::with(["archivesCategory"])->where($where)
+ ->order(["sort"=>"desc"])
+ ->paginate($paginate, false);
+ }
+
+
+ /**
+ * 创建人信息
+ *
+ * @return HasOne
+ */
+ public function member(): HasOne
+ {
+ return $this->hasOne(Member::class, 'id', 'created_by')->bind(['nickname']);
+ }
+
+
+ /**
+ * 栏目 后台用
+ *
+ * @return HasOne
+ */
+ public function category(): HasOne
+ {
+ return $this->hasOne(ArchivesCategory::class, 'id', 'category_id')->bind(['category' => 'title']);
+ }
+
+
+ /**
+ * 栏目
+ * @return HasOne
+ */
+ public function archivesCategory(): HasOne
+ {
+ return $this->hasOne(ArchivesCategory::class, 'id', 'category_id');
+ }
+
+
+ public static function onAfterInsert( $archives)
+ {
+ $archives->sort = ceil(self::max("sort")+1);
+ $archives->save();
+ }
+
+ public static function onBeforeUpdate( $archives)
+ {
+ //检查sort
+ $hasFlag = self::where([
+ ["sort","=",$archives->sort],
+ ["id","<>",$archives->id]
+ ])->find();
+ if(!empty($hasFlag)){
+ throw new RepositoryException('sort不能重复,保留小数点后2位');
+ }
+ }
+}
diff --git a/app/model/ArchivesCategory.php b/app/model/ArchivesCategory.php
new file mode 100644
index 0000000..fef4e82
--- /dev/null
+++ b/app/model/ArchivesCategory.php
@@ -0,0 +1,145 @@
+count() > 0;
+ }
+
+ /**
+ * 检测数据下 是否有子内容
+ *
+ * @param array $ids
+ * @return bool
+ */
+ public static function hasContentByIds(array $ids): bool
+ {
+ return Archives::whereIn('category_id', $ids)->count() > 0;
+ }
+
+ /**
+ * 获取列表
+ *
+ * @return ArchivesCategory[]|array|Collection
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ */
+ public static function getList()
+ {
+ return self::field('id,pid,title,name,sort,path,true as open')
+ ->order('sort', 'desc')
+ ->order('id', 'asc')
+ ->select();
+ }
+
+ /**
+ * 模型
+ *
+ * @return HasOne
+ */
+ public function model(): HasOne
+ {
+ return $this->hasOne(ArchivesModel::class, 'id', 'model_id')->bind(['model' => 'name']);
+ }
+
+
+//--王兴龙 auth start --------------------------------------------------------------------------------------------------
+ //获取前台菜单
+ public static function getListForFrontMenu()
+ {
+ $items = self::withJoin(["model"])
+ ->order(['sort' =>'desc',"id"=>"asc"])
+ ->select()
+ ->toArray();
+ return self::getCates($items);
+ }
+
+ //获取递归栏目
+ public static function getCates($cates, $parentId = 0)
+ {
+ $menus = [];
+ foreach ($cates as $cate) {
+ if ($cate['pid'] == $parentId) {
+ $children = self::getCates($cates, $cate['id']);
+ if (!empty($children)) {
+ $cate['children'] = $children;
+ }
+ $menus[] = $cate;
+ }
+ }
+ return $menus;
+ }
+
+ //当前分类的最高级分类Id
+ public static function firstGradeById($id)
+ {
+ $res = 0;
+ $item = self::getById($id);
+ if ($item) {
+ $res = $id;
+ if ($item['pid'] > 0) {
+ $items = self::select()->toArray();
+ $first = self::getFirstGrade($items, $item['pid']);
+ if (!empty($first)) {
+ $res = $first['id'];
+ }
+ }
+ }
+ return $res;
+ }
+
+ public static function getFirstGrade($items, $parentId)
+ {
+ $data = [];
+ foreach ($items as $key => $item) {
+ if ($item['id'] == $parentId) {
+ if ($item['pid'] > 0) {
+ unset($items[$key]);
+ $parent = self::getFirstGrade($items, $item['pid']);
+ if (!empty($parent)) {
+ $data = $parent;
+ } else {
+ $data = $item;
+ }
+ } else {
+ $data = $item;
+ }
+ }
+ }
+ return $data;
+ }
+
+ /**
+ * 获取每个栏目文章封面大小 数组
+ * */
+ public static function getArchiveCoverSizeArr()
+ {
+ return json_encode(self::column(["id","archive_cover_size"],"id"));
+ }
+ /**
+ * 获取每个栏目 设置路由
+ * */
+ public static function getRoute()
+ {
+ return self::column(["id","name","route"],"id");
+ }
+
+//--王兴龙 auth end --------------------------------------------------------------------------------------------------
+}
diff --git a/app/model/ArchivesModel.php b/app/model/ArchivesModel.php
new file mode 100644
index 0000000..c90dd7d
--- /dev/null
+++ b/app/model/ArchivesModel.php
@@ -0,0 +1,19 @@
+"1",
+ ];
+ }
+}
\ No newline at end of file
diff --git a/app/model/ArchivesModelField.php b/app/model/ArchivesModelField.php
new file mode 100644
index 0000000..05c75e9
--- /dev/null
+++ b/app/model/ArchivesModelField.php
@@ -0,0 +1,118 @@
+count() <= 0) {
+ $rs = Db::query("SHOW FULL COLUMNS FROM ".Archives::ORIGINAL_TABLE);
+ $insert = [];
+ foreach ($rs as $val) {
+ $arr = [];
+ $arr['model_id'] = $modelId;
+ $arr['model'] = $modelName;
+ $arr['name'] = $val['Field'];
+ $arr['title'] = $val['Comment'];
+ $arr['remark'] = $val['Comment'];
+ $insert[] = $arr;
+ }
+
+ (new self())->saveAll($insert);
+ }
+ }
+
+ /**
+ * 同步字段
+ *
+ * @param int $modelId
+ * @param string $modelName
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ */
+ public static function syncFieldList(int $modelId, string $modelName)
+ {
+ $rs = Db::query("SHOW FULL COLUMNS FROM ".Archives::ORIGINAL_TABLE);
+ $oldFieldList = self::where('model_id', $modelId)->where('is_virtual', self::VIRTUAL_NO)->select();
+ $oldFields = $oldFieldList->column('name');
+
+
+ $newestFields = [];
+ foreach ($rs as $val) {
+ $newestFields[] = $val['Field'];
+ }
+
+ //待删除字段
+ $delete = array_diff($oldFields, $newestFields);
+
+ //待新增字段
+ $needInsertFields = array_diff($newestFields, $oldFields);
+
+ $insert = [];//新增字段
+ foreach ($rs as $val) {
+ if (in_array($val['Field'], $needInsertFields)) {
+ $arr = [];
+ $arr['model_id'] = $modelId;
+ $arr['model'] = $modelName;
+ $arr['name'] = $val['Field'];
+ $arr['title'] = $val['Comment'];
+ $arr['remark'] = $val['Comment'];
+ $insert[] = $arr;
+ }
+ }
+
+ (new self())->saveAll($insert);
+ (new self())->whereIn('name', $delete)->delete();
+ }
+
+ /**
+ * 各栏目[模型] 可展示字段列表
+ *
+ * @return array
+ */
+ public static function showFieldList(): array
+ {
+ $list = self::alias('amf')
+ ->leftJoin('archives_category ac', 'ac.model_id = amf.model_id')
+ ->field('amf.*, ac.id as category_id, ac.title as category_title')
+ ->where('amf.status', self::COMMON_ON)
+ ->where('ac.id', '>', 0)
+ ->select();
+
+ $res = [];
+
+ $list = $list->toArray();
+ foreach ($list as $item) {
+ if (!isset($res[$item['category_id']])) {
+ $res[$item['category_id']] = [];
+ }
+
+ $res[$item['category_id']][] = $item['name'];
+ }
+
+ return $res;
+ }
+}
\ No newline at end of file
diff --git a/app/model/Attachment.php b/app/model/Attachment.php
new file mode 100644
index 0000000..229f4d9
--- /dev/null
+++ b/app/model/Attachment.php
@@ -0,0 +1,69 @@
+ $p) {
+ $currentPath = '/';
+ $currentArr = array_slice($pathArr, 0, $k);
+ if ($currentArr) {
+ $currentPath = "/".implode('/', $currentArr)."/";
+ }
+ if ($currentPath != '/' && self::where('path', $currentPath)->where('name', $p)->count() <= 0) {
+ $arr = [];
+ $arr['path'] = $currentPath;
+ $arr['name'] = $p;
+ $arr['created_at'] = $now;
+ $arr['is_dir'] = self::COMMON_ON;
+ $arr['type'] = self::TYPE_DIR;
+
+ $insert[] = $arr;
+ }
+ }
+
+ (new self())->saveAll($insert);
+ return true;
+ } catch (Exception $e) {
+ return false;
+ }
+ }
+}
diff --git a/app/model/Base.php b/app/model/Base.php
new file mode 100644
index 0000000..1900ec3
--- /dev/null
+++ b/app/model/Base.php
@@ -0,0 +1,390 @@
+select()->toArray();
+ }
+
+ //根据ID获取单条数据
+ public static function getById($id)
+ {
+ if ($id <= 0) {
+ return [];
+ }
+ return self::where('id', $id)->findOrEmpty()->toArray();
+ }
+
+ /**
+ * 通过ID查找记录
+ *
+ * @param int $id
+ * @param array $fields
+ * @param callable|null $call
+ * @return array|Model|null
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ */
+ public static function findById(int $id, array $fields = [], callable $call = null)
+ {
+ $q = self::when(!empty($fields), function ($q) use ($fields) {
+ $q->field($fields);
+ });
+ if ($call !== null) {
+ $q = $call($q);
+ }
+ return $q->find($id);
+ }
+
+ /**
+ * 查找单个记录
+ *
+ * @param array $where
+ * @param array $fields
+ * @param callable|null $call
+ * @return array|Model|null
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ */
+ public static function findOne(array $where = [], array $fields = [], callable $call = null)
+ {
+ $q = self::when(!empty($fields), function ($q) use ($fields) {
+ $q->field($fields);
+ })->where($where);
+
+ if ($call !== null) {
+ $q = $call($q);
+ }
+ return $q->find();
+ }
+
+ //根据ID更新数据
+ public static function updateById($id, $data)
+ {
+ return self::where('id', $id)->update($data);
+ }
+
+ //根据where条件和排序获取记录
+ public static function getListByWhereAndOrder($where, $order, $limit = 1)
+ {
+ return self::where($where)
+ ->order($order)
+ ->limit($limit)
+ ->select()
+ ->toArray();
+ }
+
+ /**
+ * 根据ID删除数据
+ *
+ * @param int $id
+ * @return bool
+ */
+ public static function deleteById(int $id): bool
+ {
+ return self::where('id', $id)->delete();
+ }
+
+ /**
+ * 根据ID列表删除数据
+ *
+ * @param array $ids
+ * @return bool
+ */
+ public static function deleteByIds(array $ids): bool
+ {
+ return self::whereIn('id', $ids)->delete();
+ }
+
+ /**
+ * 排序
+ *
+ * @param int $id 调整ID
+ * @param string $type 本次操作类型 向上、向下
+ * @param int $num 移动位数
+ * @param string $listType 列表的排序方式 默认为降序
+ * @param array $where 额外条件 如限制在指定分类下 where[] = ['category_id', '=', 6]
+ * @return array
+ * @throws Exception
+ */
+ public static function sort(int $id, string $type, int $num = 1, string $listType = 'desc', array $where = []): array
+ {
+ $res = ['code' => 0, 'msg' => 'success'];
+ $item = self::getById($id);
+
+ if (!$item) {
+ $res['code'] = 1;
+ $res['msg'] = '记录不存在';
+ return $res;
+ }
+
+ if ($listType == 'desc') {
+ if ($type == 'down') {
+ $where[] = ['sort', '<', $item['sort']];
+ $order = "sort desc";
+ } else {
+ $where[] = ['sort', '>', $item['sort']];
+ $order = "sort asc";
+ }
+ } else {
+ if ($type == 'up') {
+ $where[] = ['sort', '<', $item['sort']];
+ $order = "sort desc";
+ } else {
+ $where[] = ['sort', '>', $item['sort']];
+ $order = "sort asc";
+ }
+ }
+
+ $forSortItems = self::getListByWhereAndOrder($where, $order, $num);
+ if (!empty($forSortItems)) {
+ $updateData = [];
+ $forSortCount = count($forSortItems);
+ for ($i = 0; $i < $forSortCount; $i++) {
+ if ($i == 0) {
+ $updateData[] = [
+ 'id' => $forSortItems[$i]['id'],
+ 'sort' => $item['sort']
+ ];
+ } else {
+ $updateData[] = [
+ 'id' => $forSortItems[$i]['id'],
+ 'sort' => $forSortItems[$i - 1]['sort']
+ ];
+ }
+ }
+ $updateData[] = [
+ 'id' => $item['id'],
+ 'sort' => $forSortItems[$i - 1]['sort']
+ ];
+
+ if (!empty($updateData)) {
+ $obj = new static();
+ $obj->saveAll($updateData);
+ return $res;
+ }
+ }
+ $res['code'] = 1;
+ $res['msg'] = '无需调整';
+ return $res;
+ }
+
+ /**
+ * 查询列表 [带分页 适用于后台]
+ *
+ * @param array $data 查询数据 格式如下
+ * [
+ * 'fields' => ['id','title','desc'],//查询字段
+ * 'where' => [
+ * ['name', 'like', '%thinkphp%'],
+ * ['title', 'like', '%thinkphp%'],
+ * ['id', '>', 0],
+ * ['status', '=', 1],
+ * ],//查询条件
+ * 'order' => ['order'=>'desc','id'=>'desc'],//排序
+ * 'size' => 50,//每页数量
+ * ]
+ * @param array $pageParams 分页参数 具体参考:https://www.kancloud.cn/manual/thinkphp6_0/1037638
+ * @param callable|null $callback 复杂查询条件 使用闭包查询 此时建议data留空
+ * @return Paginator
+ * @throws DbException
+ */
+ public static function findListWithPaginate(array $data = [], array $pageParams = [], callable $callback = null): Paginator
+ {
+ $q = new static();
+ $fields = isset($data['fields']) && !empty($data['fields']) ? $data['fields'] : [];
+ $where = isset($data['where']) && !empty($data['where']) ? $data['where'] : [];
+ $order = isset($data['order']) && !empty($data['order']) ? $data['order'] : [];
+ $limit = $data['size'] ?? 20;
+
+ if (count($where)) {
+ $q = $q->where($where);
+ }
+
+ if (count($fields)) {
+ $q = $q->field($fields);
+ }
+
+ if ($callback) {
+ $q = $callback($q);
+ }
+
+ if (count($order)) {
+ $q = $q->order($order);
+ }
+
+ $pageParams['list_rows'] = $limit;
+
+ return $q->paginate($pageParams);
+ }
+
+ /**
+ * 查询列表
+ *
+ * @param array $simpleWhere 简易查询条件
+ * @param array $fields 查询字段 []表示全部
+ * @param int $page 默认第一页 0不限制
+ * @param int $limit 限制条数 0不限制
+ * @param callable|null $callback 复杂的条件 使用闭包查询
+ * @param array $orders 键值对,排序
+ * @return array
+ * @throws Exception
+ */
+ public static function findList(array $simpleWhere = [], array $fields = [], int $page = 1, int $limit = 0, callable $callback = null, array $orders = []): array
+ {
+ $q = new static();
+ $data = [
+ 'total' => 0,
+ 'current' => $page,
+ 'size' => $limit,
+ 'list' => new Collection(),
+ ];
+
+ if (count($fields)) {
+ $q = $q->field($fields);
+ }
+
+ if (count($simpleWhere)) {
+ $q = $q->where($simpleWhere);
+ }
+
+ if ($callback) {
+ $q = $callback($q);
+ }
+
+ $data['total'] = $q->count();
+
+ if ($data['total']) {
+ if (count($orders)) {
+ $q = $q->order($orders);
+ }
+ if ($page) {
+ $q = $q->page($page);
+ }
+ if ($limit == 0) {
+ $q = $q->limit(1000);
+ } else {
+ $q = $q->limit($limit);
+ }
+
+ $data['list'] = $q->select();
+ }
+
+ return $data;
+ }
+
+ /**
+ * 获取路径
+ *
+ * @param int $pid
+ * @return string
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ */
+ public static function getPath(int $pid): string
+ {
+ if ($pid == 0) {
+ $path = ',0,';
+ } else {
+ $parent = self::findById($pid);
+ if (empty($parent)) {
+ $path = ',0,';
+ } else {
+ $path = $parent['path'].$parent['id'].',';
+ }
+ }
+
+ return $path;
+ }
+
+ /**
+ * 刷新路径
+ *
+ * @param int $pid
+ * @param array $data 默认全部 若data不为空 至少包含[id,path, $pidField]
+ * @param string $pidField 父级ID字段名 默认 pid
+ * @throws Exception
+ */
+ public static function refreshPath(int $pid = 0, array $data = [], string $pidField = 'pid')
+ {
+ $data = !empty($data) ? $data : self::column('id,path,'.$pidField);
+ $updateAllPaths = [];
+ self::recursionPath($pid, $data, $updateAllPaths);
+
+ (new static())->saveAll($updateAllPaths);
+ }
+
+ /**
+ * 获取递归最新路径
+ *
+ * @param int $pid
+ * @param array $data 全部数据 尽量传全部数据
+ * @param array $paths
+ */
+ public static function recursionPath(int $pid, array $data, array &$paths = [])
+ {
+ foreach ($data as $k => $v) {
+ if ($pid == $v['pid']) {
+ $arr = [];
+ $arr['id'] = $v['id'];
+ if ($pid == 0) {
+ $arr['path'] = ',0,';
+ } else {
+ $arr['path'] = $paths[$v['pid']]['path'].$v['pid'].',';
+ }
+ $paths[$v['id']] = $arr;
+
+ unset($data[$k]);
+
+ self::recursionPath($v['id'], $data, $paths);
+ }
+ }
+ }
+
+ /**
+ * 获取所有后代ID[包含孙级] 仅对拥有path字段的表生效
+ *
+ * @param int $id
+ * @return array
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ */
+ public static function getAllChildrenIds(int $id): array
+ {
+ $item = self::find($id);
+ if ($item && isset($item['path'])) {
+ $path = $item['path'].$id.',';
+ return self::where('path', 'like', $path.'%')->column('id');
+ } else {
+ return [];
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/model/Block.php b/app/model/Block.php
new file mode 100644
index 0000000..e9d38e4
--- /dev/null
+++ b/app/model/Block.php
@@ -0,0 +1,147 @@
+ '富文本',
+ self::TEXT => '纯文本',
+ self::IMG => '图片',
+ self::GROUP => '多图',
+ self::FILE => '文件',
+ self::VIDEO => '视频',
+ self::ING_LIST => '图文列表',
+ ];
+ }
+
+ //根据键值和类目获取数据
+ public static function getByKeyword($keyword, $categoryId)
+ {
+ return self::where('keyword', $keyword)->where('category_id', $categoryId)->findOrEmpty()->toArray();
+ }
+
+ //根据栏目ID获取块列表
+ public static function getByCategoryId($categoryId)
+ {
+
+ if($categoryId <= 0){
+ return [];
+ }
+
+ $items = self::where('category_id', $categoryId)
+ ->order('id desc')
+ ->select()
+ ->toArray();
+ if(empty($items)){
+ return [];
+ }
+ $data = [];
+ foreach($items as $item){
+ $data[$item['name']] = $item;
+ }
+ return $data;
+ }
+
+ //获取在使用中的文件
+ public static function getFilesInUse()
+ {
+ $items = self::whereIn('type', [self::IMG, self::GROUP, self::VIDEO, self::TEXT])
+ ->select()
+ ->toArray();
+ $data = [];
+ foreach($items as $item){
+ if($item['type'] == self::IMG){
+ $file = trim($item['value']);
+ if(!empty($file)){
+ $key = getKeyByPath($file);
+ $data[$key] = $file;
+ }
+ }elseif($item['type'] == self::GROUP){
+ $val = trim($item['value']);
+ if (!empty($val) && $val != '[]' && $val != 'null') {
+ $files = json_decode($val, true);
+ foreach($files as $file){
+ $src = trim($file['src']);
+ if(!empty($src)){
+ $key = getKeyByPath($src);
+ $data[$key] = $src;
+ }
+ }
+ }
+ }elseif($item['type'] == self::VIDEO){
+ $video = trim($item['value']);
+ if(!empty($video)){
+ $key = getKeyByPath($video);
+ $data[$key] = $video;
+ }
+ $img = trim($item['img']);
+ if(!empty($img)){
+ $key = getKeyByPath($img);
+ $data[$key] = $img;
+ }
+ }elseif($item['type'] == self::TEXT){
+ $imgs = getImageUrlFromText($item['value']);
+ if(!empty($imgs)){
+ $data = array_merge($data, $imgs);
+ }
+ $videos = getVideoUrlFromText($item['value']);
+ if(!empty($videos)){
+ $data = array_merge($data, $videos);
+ }
+ }
+ }
+ return $data;
+ }
+
+ /**
+ * 栏目
+ *
+ * @return HasOne
+ */
+ public function category(): HasOne
+ {
+ return $this->HasOne(ArchivesCategory::class, 'id', 'category_id');
+ }
+
+ /**
+ * 设置内容
+ * */
+ public function setContentAttr($value,$data)
+ {
+ if($data['type'] == self::ING_LIST){
+ $content = array_column($value,null,"sort");
+ $contentKey = array_keys($content);
+ sort($contentKey);
+ $contentData =[];
+ foreach ($contentKey as $ck){
+ $contentData[]= $content[$ck];
+ }
+ return json_encode($contentData);
+ }
+ return $value;
+ }
+
+ /**
+ * 设置内容
+ * */
+ public function getContentAttr($value,$data)
+ {
+ if($data['type'] == self::ING_LIST){
+ return json_decode($value,true)
+ }
+ return $value;
+ }
+}
diff --git a/app/model/Config.php b/app/model/Config.php
new file mode 100644
index 0000000..4aae7a5
--- /dev/null
+++ b/app/model/Config.php
@@ -0,0 +1,19 @@
+hasOne(Account::class, 'id', 'account_id');
+ }
+
+}
\ No newline at end of file
diff --git a/app/model/File.php b/app/model/File.php
new file mode 100644
index 0000000..2141df1
--- /dev/null
+++ b/app/model/File.php
@@ -0,0 +1,129 @@
+ '图片',
+ 'video' => '视频',
+ 'file' => '文件'
+ ];
+ }
+
+ //获取文件列表
+ public static function getList($type = '', $page = 1, $per = 20)
+ {
+ $limit = ($page - 1) * $per;
+ if ($type != '') {
+ if (!in_array($type, array_keys(self::getTypes()))) {
+ return [];
+ }
+ $items = self::where('type', $type)
+ ->order('id desc');
+ } else {
+ $items = self::order('id desc');
+ }
+ $items = $items->limit($limit, $per)->select()->toArray();
+ foreach ($items as &$item) {
+ $item['sizeStr'] = sizeToStr($item['size']);
+ }
+ return $items;
+ }
+
+ //获取分页列表
+ public static function getListPage($type = '', $per = 20)
+ {
+ if ($type != '') {
+ if (!in_array($type, array_keys(self::getTypes()))) {
+ return [];
+ }
+ return self::where('type', $type)
+ ->order('id desc')
+ ->paginate([
+ 'list_rows' => $per,
+ 'query' => [
+ 'type' => $type
+ ]
+ ], false);
+ } else {
+ return self::order('id desc')
+ ->paginate([
+ 'list_rows' => $per
+ ], false);
+ }
+ }
+
+ //添加,$w_h图片尺寸大小,单位像素,只对type=img时有效
+ public static function add($file, $src, $md5, $type = 'image')
+ {
+ $realPath = public_path().ltrim($src, '/');
+ $oss = false;
+ if (is_file($realPath) && $type == 'image') {
+ $img = Image::open($realPath);
+ list($w, $h) = $img->size();
+ $w_h = $w.'px * '.$h.'px';
+ } else {
+ $w_h = '';
+ }
+
+ $now = date('Y-m-d H:i:s');
+ Attachment::pathDirHandle($src);
+
+ Config::load('extra/base', 'base');
+ $baseConfig = config('base');
+ if (isset($baseConfig['oss']) && $baseConfig['oss'] == 'true') {
+ $ossObject = AliOss::instance();
+ try {
+ $pathInfo = pathinfo($src);
+
+ $ossConfig = AliOss::config();
+ $bucket = $ossConfig['bucket'];
+ //是否存在
+ if (!$ossObject->doesObjectExist($bucket, ltrim($src, '/'))) {
+ //创建目录
+ $ossObject->createObjectDir($bucket, ltrim($pathInfo['dirname'], '/'));
+
+ $ossObject->uploadFile($bucket, ltrim($src, '/'), $realPath);
+ }
+ $oss = true;
+ } catch (Exception $e) {
+ \think\facade\Log::error('阿里云OSS上传文件失败 '.$e->getMessage());
+ }
+ }
+
+ // 将src中路径创建
+ return self::create([
+ 'type' => $type,
+ 'name' => $file->getOriginalName(),
+ 'md5' => $md5,
+ 'src' => $src,
+ 'path' => isset(pathinfo($src)['dirname']) ? pathinfo($src)['dirname'].'/' : '',
+ 'size' => $file->getSize(),
+ 'suffix' => $file->getOriginalExtension(),
+ 'mime_type' => $file->getOriginalMime(),
+ 'created_at' => $now,
+ 'updated_at' => $now,
+ 'is_oss' => $oss,
+ 'w_h' => $w_h
+ ]);
+ }
+
+ //获取所有记录
+ public static function getAll()
+ {
+ return self::select()->toArray();
+ }
+}
diff --git a/app/model/Link.php b/app/model/Link.php
new file mode 100644
index 0000000..ab8f602
--- /dev/null
+++ b/app/model/Link.php
@@ -0,0 +1,39 @@
+delete();
+ }
+
+ //获取友情链接
+ public static function getList()
+ {
+ return self::order('sort asc')
+ ->select()
+ ->toArray();
+ }
+
+ public static function onAfterInsert($item)
+ {
+ $item->sort = $item->id;
+ $item->save();
+ }
+
+ //获取友情链接涉及到的文件
+ public static function getFilesInUse()
+ {
+ $items = self::select()->toArray();
+ $data = [];
+ foreach($items as $item){
+ $src = trim($item['src']);
+ if(!empty($src)){
+ $key = getKeyByPath($src);
+ $data[$key] = $src;
+ }
+ }
+ return $data;
+ }
+}
diff --git a/app/model/Log.php b/app/model/Log.php
new file mode 100644
index 0000000..868791c
--- /dev/null
+++ b/app/model/Log.php
@@ -0,0 +1,41 @@
+ $auth['user_id'] ?? 0,
+ 'name' => $auth['username'] ?? 0,
+ 'ip' => request()->ip(),
+ 'create_time' => time(),
+ 'controller' => $controller,
+ 'request_type' => $requestType,
+ 'action' => $action,
+ 'content' => $content
+ ]);
+ }
+
+ /**
+ * @return HasOne
+ */
+ public function memberName(): HasOne
+ {
+ return $this->hasOne(Member::class, 'id', 'member_id')->bind(['operator' => 'nickname']);
+ }
+
+ public function getCreateTimeAttr($value)
+ {
+ if (empty($value)) {
+ return $value;
+ }
+
+ return date('Y-m-d H:i:s', $value);
+ }
+}
\ No newline at end of file
diff --git a/app/model/LoginLog.php b/app/model/LoginLog.php
new file mode 100644
index 0000000..35bb334
--- /dev/null
+++ b/app/model/LoginLog.php
@@ -0,0 +1,6 @@
+leftjoin('auth_group g', 'g.id=m.group_id')
+ ->field('m.id,m.username,m.login_time,m.group_id,g.title')
+ ->order('m.id', 'asc')
+ ->paginate($limit);
+ }
+
+ /**
+ * 根据角色分组返回用户
+ * @param int $groupId 角色分组ID
+ * @param int $limit 每页数量
+ */
+ public static function getListByGroup($groupId, $limit = 40)
+ {
+ return self::alias('m')
+ ->leftjoin('auth_group g', 'g.id=m.group_id')
+ ->field('m.id,m.username,m.login_time,m.group_id,g.title')
+ ->where('m.group_id', '=', $groupId)
+ ->order('m.id', 'asc')
+ ->paginate($limit);
+ }
+
+ //根据用户名获取管理账号
+ public static function getByUserName($username)
+ {
+ return self::where('username', trim($username))
+ ->findOrEmpty()
+ ->toArray();
+ }
+
+ //根据ID获取管理账户和相关权限
+ public static function getMemberAndRulesByID($memberId)
+ {
+ return self::alias('m')
+ ->join('auth_group g', 'm.group_id = g.id', 'LEFT')
+ ->field('m.group_id,g.rules')
+ ->where('m.id', $memberId)
+ ->findOrEmpty()
+ ->toArray();
+ }
+
+ public static function updateCates($id, $cates)
+ {
+ $cates = implode(',', $cates);
+ $data = ['cates' => $cates];
+ self::updateById($id, $data);
+ }
+}
\ No newline at end of file
diff --git a/app/model/Menu.php b/app/model/Menu.php
new file mode 100644
index 0000000..3e60209
--- /dev/null
+++ b/app/model/Menu.php
@@ -0,0 +1,71 @@
+ '查看',
+ 'add' => '添加',
+ 'edit' => '编辑',
+ 'del' => '删除',
+ 'sort' => '排序',
+ 'modify' => '属性设置',
+ ];
+ }
+
+ /**
+ * 自从生成常规操作权限
+ *
+ * @param int $id
+ * @param string $name
+ * @param string $path
+ * @throws Exception
+ */
+ public static function generate(int $id, string $name, string $path)
+ {
+ $actions = self::defaultAction();
+ $delete = [];
+ $insert = [];
+ $created = date('Y-m-d H:i:s');
+ foreach ($actions as $key => $action) {
+ $name = explode(':', $name)[0];
+ $delete[] = $name.':'.$key;
+
+ $arr = [];
+ $arr['title'] = $action;
+ $arr['pid'] = $id;
+ $arr['name'] = $name.':'.$key;
+ $arr['type'] = self::TYPE_ACTION;
+ $arr['path'] = $path.$id.',';
+ $arr['remark'] = sprintf("自动生成[%s][%s]操作", $name, $action);
+ $arr['created_at'] = $created;
+
+ $insert[] = $arr;
+ }
+
+ //删除已有常规操作
+ self::where('pid', $id)->whereIn('name', $delete)->delete();
+
+ //新增常规操作
+ (new self())->saveAll($insert);
+ }
+}
diff --git a/app/model/Model.php b/app/model/Model.php
new file mode 100644
index 0000000..7bc8c1e
--- /dev/null
+++ b/app/model/Model.php
@@ -0,0 +1,18 @@
+select()
+ ->toArray();
+ }
+ public static function onAfterInsert($model)
+ {
+ $model->sort = $model->id;
+ $model->save();
+ }
+}
\ No newline at end of file
diff --git a/app/model/Role.php b/app/model/Role.php
new file mode 100644
index 0000000..504c5a1
--- /dev/null
+++ b/app/model/Role.php
@@ -0,0 +1,8 @@
+ '在售',
+ (string)self::STATUS_OFF => '已下架',
+ ];
+ }
+
+ public static function allowScoreTextList(): array
+ {
+ return [
+ (string)self::ALLOW_ONLY_BUY => '购买',
+ (string)self::ALLOW_BUY_AND_SCORE => '购买或积分兑换',
+ (string)self::ALLOW_ONLY_SCORE => '积分兑换',
+ ];
+ }
+
+ public static function onAfterInsert(Model $item)
+ {
+ $item->sort = $item->id;
+ $item->save();
+ }
+
+ /**
+ * 模型关联:商品分类
+ *
+ * @return HasOne
+ */
+ public function category(): HasOne
+ {
+ return $this->hasOne(SkuCategory::class, 'id', 'category_id');
+ }
+
+ /**
+ * 生成 SKU coding
+ * 年份 + 2位随机码 + 时间戳(秒[10位] + 微妙[4位])
+ * @length 18
+ */
+ public static function generateCoding(): string
+ {
+ $randStr = str_pad(mt_rand(1,99), 2, '0', STR_PAD_LEFT);
+ $prefix = date('y').$randStr;
+ $mt = microtime(true);
+ list($time, $micro) = explode('.', $mt);
+ $micro = str_pad($micro, 4, '0', STR_PAD_RIGHT);
+ $micro = substr($micro, 0, 4);
+
+ return $prefix.$time.$micro;
+ }
+
+ /**
+ * @return HasOne
+ */
+ public function spu(): HasOne
+ {
+ return $this->hasOne(Spu::class, 'id', 'spu_id');
+ }
+
+ /**
+ * 关联活动商品
+ *
+ * @return HasOne
+ */
+ public function activity(): HasOne
+ {
+ return $this->hasOne(SpuActivity::class, 'id', 'spu_activity_id');
+ }
+}
\ No newline at end of file
diff --git a/app/model/Slide.php b/app/model/Slide.php
new file mode 100644
index 0000000..b6b795f
--- /dev/null
+++ b/app/model/Slide.php
@@ -0,0 +1,40 @@
+delete();
+ }
+
+ //获取幻灯片列表
+ public static function getList()
+ {
+ return self::order("sort asc")
+ ->select()
+ ->toArray();
+ }
+
+ public static function onAfterInsert($slide)
+ {
+ $slide->sort = $slide->id;
+ $slide->save();
+ }
+
+ //获取轮播图涉及到的文件
+ public static function getFilesInUse()
+ {
+ $items = self::select()->toArray();
+ $data = [];
+ foreach ($items as $item) {
+ $src = trim($item['src']);
+ if (!empty($src)) {
+ $key = getKeyByPath($src);
+ $data[$key] = $src;
+ }
+ }
+ return $data;
+ }
+}
diff --git a/app/model/SlidePosition.php b/app/model/SlidePosition.php
new file mode 100644
index 0000000..a598bf9
--- /dev/null
+++ b/app/model/SlidePosition.php
@@ -0,0 +1,17 @@
+ $slideSize,
+ 'slide_mobile_size' => $slideMobileSize
+ ];
+ }
+ //获取文章图片尺寸
+ public static function getArticleImageSize()
+ {
+ $system = self::getSystem();
+ if(!empty($system['article_w']) && !empty($system['article_h'])){
+ $articleSize = $system['article_w'] . '像素 X ' . $system['article_h'] . '像素';
+ }else{
+ $articleSize = '';
+ }
+ return $articleSize;
+ }
+ //获取系统配置信息
+ public static function getSystem()
+ {
+ return self::order('id asc')
+ ->findOrEmpty()
+ ->toArray();
+ }
+}
diff --git a/app/repository/AccountRepository.php b/app/repository/AccountRepository.php
new file mode 100644
index 0000000..253f849
--- /dev/null
+++ b/app/repository/AccountRepository.php
@@ -0,0 +1,1070 @@
+findList();
+ }
+
+ /**
+ * 获取指定账户记录By手机号
+ *
+ * @param string $phone
+ * @param array $fields
+ * @return Model|null
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ */
+ public function infoByPhone(string $phone, array $fields = []): ?Model
+ {
+ $where[] = ['mobile', '=', $phone];
+ return $this->findOneByWhere($where, $fields);
+ }
+
+ /**
+ * 获取指定账户记录By用户名
+ *
+ * @param string $username
+ * @param array $fields
+ * @return Model|null
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ */
+ public function infoByUsername(string $username, array $fields = []): ?Model
+ {
+ $where[] = ['username', '=', $username];
+ return $this->findOneByWhere($where, $fields);
+ }
+
+ /**
+ * 混合查找记录
+ *
+ * @param string $username 混合查询 手机号或用户名
+ * @return array|Model|null
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ */
+ public function findByHybrid(string $username)
+ {
+ return $this->model->whereOr('username', $username)->whereOr('mobile', $username)->find();
+ }
+
+ /**
+ * 通过微信小程序的openID查询
+ *
+ * @param string $openID
+ * @return array|Model|null
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ */
+ public function findByOpenID(string $openID)
+ {
+ return $this->model->where('openid', $openID)->find();
+ }
+
+ /**
+ * 通过个人邀请码查询用户信息
+ *
+ * @param string $inviteCode
+ * @return array|Model|null
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ */
+ public function findByInviteCode(string $inviteCode)
+ {
+ if (empty($inviteCode)) {
+ return null;
+ }
+ return $this->model->where('invite_code', $inviteCode)->find();
+ }
+
+ /**
+ * 通过个人编号查询用户信息
+ *
+ * @param string $coding
+ * @return array|Model|null
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ */
+ public function findByUserCoding(string $coding)
+ {
+ if (empty($coding)) {
+ return null;
+ }
+ return $this->model->where('coding', $coding)->find();
+ }
+
+ /**
+ * 修改密码
+ *
+ * @param int $accountId
+ * @param string $oldPwd
+ * @param string $newPwd
+ * @return bool
+ * @throws RepositoryException
+ */
+ public function modifyPwd(int $accountId, string $oldPwd, string $newPwd): bool
+ {
+ if (!$user = $this->findById($accountId)) {
+ throw new RepositoryException('用户不存在');
+ }
+
+ if ($user['password'] != md5($oldPwd)) {
+ throw new RepositoryException('原密码错误');
+ }
+
+ $user->password = md5($newPwd);
+
+ return $user->save();
+ }
+
+ /**********************************************
+ * TODO 分割线,上述与本项目无关的代码需要清除
+ *********************************************/
+
+ /**
+ * 修改用户数据
+ *
+ * @param int $accountId
+ * @param array $fieldsKV 修改内容:键值对
+ * @return mixed
+ * @throws RepositoryException
+ */
+ public function accountEditInfo(int $accountId, array $fieldsKV)
+ {
+ $allowFields = ['headimgurl', 'real_name', 'nickname', 'mobile', 'gender', 'province', 'city', 'county', 'birthday'];
+ $fieldsKV = arrayKeysFilter($fieldsKV, $allowFields);
+
+ if (isset($fieldsKV['mobile']) && !empty($fieldsKV['mobile'])) {
+ if (!checkMobile($fieldsKV['mobile'])) {
+ throw new RepositoryException('手机号不正确');
+ }
+ }
+
+ if (!$account = $this->findById($accountId)) {
+ throw new RepositoryException('用户信息错误');
+ }
+
+ return $account->save($fieldsKV);
+ }
+
+ /**
+ * 统计用户分销人数(二级)
+ */
+ public function shareAccountCount(int $accountId): array
+ {
+ $data = [
+ 'first' => 0,
+ 'second' => 0,
+ 'total' => 0,
+ ];
+ try {
+ $data['first'] = ShareRegLog::where('inviter_account_id', $accountId)->where('is_active', ShareRegLog::COMMON_ON)->count();
+ $data['second'] = ShareRegLog::where('inviter_parent_account_id', $accountId)->where('is_active', ShareRegLog::COMMON_ON)->count();
+ $data['total'] = $data['first'] + $data['second'];
+ } catch (Exception $e) {
+
+ }
+
+ return $data;
+ }
+
+
+ /**
+ * 添加分享注册关系绑定日志
+ *
+ * @param int $accountId
+ * @param int $inviterId
+ * @param string $grade 分享绑定关系层级
+ * @param bool $accountActive 是否为有效用户
+ * @return bool
+ */
+ public function addShareRegLogBak(int $accountId, int $inviterId, string $grade, bool $accountActive = true): bool
+ {
+ if ($accountId <= 0 || $inviterId <= 0) {
+ return false;
+ }
+
+ Db::startTrans();
+ try {
+ $shareConf = ExtraConfig::share();
+ $score = $shareConf[$grade] ?? 0;
+
+ $regLog = ShareRegLog::where('reg_account_id', $accountId)
+ ->where('inviter_account_id', $inviterId)
+ ->find();
+
+ $isCreate = false;
+ $isUpdate = false;
+
+ if ($accountActive) {
+ if (!$regLog) {
+ $isCreate = true;
+ } elseif ($regLog['score'] == 0) {
+ // 用户激活后,获得积分
+ $isUpdate = true;
+ }
+
+ } else {
+ if ($regLog) {
+ throw new RepositoryException('已有绑定记录!');
+ } else {
+ // 未有效激活的用户,相关绑定的人员只有在该用户成功激活后才能获得积分
+ $score = 0;
+ $isCreate = true;
+ }
+ }
+
+ switch ($grade) {
+ case self::SHARE_CURRENT:
+ $grade = ShareRegLog::SHARE_GRADE_FIRST;
+ break;
+ case self::SHARE_PARENT:
+ $grade = ShareRegLog::SHARE_GRADE_SECOND;
+ break;
+ case self::SHARE_SERVICE:
+ $grade = ShareRegLog::SHARE_GRADE_SERVICE;
+ break;
+ default:
+ return false;
+ }
+
+ if ($isCreate) {
+ ShareRegLog::create([
+ 'reg_account_id' => $accountId,
+ 'inviter_account_id' => $inviterId,
+ 'grade' => $grade,
+ 'score' => $score,
+ 'created_at' => date('Y-m-d H:i:s')
+ ]);
+ } elseif ($isUpdate) {
+ $regLog->save(['score' => $score]);
+ }
+
+ if ($isCreate || $isUpdate) {
+ $inviter = $this->model->where('id', $inviterId)->lock(true)->find();
+ if ($inviter) {
+ Account::updateById($inviterId, [
+ 'score' => $inviter['score'] + $score
+ ]);
+ }
+ }
+
+ Db::commit();
+ } catch (RepositoryException | Exception $e) {
+ Db::rollback();
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * 注册时邀请关系绑定
+ * 存在邀请人时。邀请人、邀请人当前的客服、邀请人的邀请人三者均可获得相应积分
+ *
+ * @param int $regAccountId 注册人ID
+ * @param int $inviterId 邀请人ID
+ * @param int $inviterParentAid 邀请人的邀请人ID
+ * @param int $serviceId 邀请人的当前客服ID
+ * @return bool
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ */
+ public function addShareRegLog(int $regAccountId, int $inviterId, int $inviterParentAid = 0, int $serviceId = 0): bool
+ {
+ if ($regAccountId <= 0 || $inviterId <= 0) {
+ return false;
+ }
+
+ $shareConf = ExtraConfig::share();
+
+ if (ShareRegLog::where('reg_account_id', $regAccountId)->find()) {
+ return true;
+ }
+
+ ShareRegLog::create([
+ 'reg_account_id' => $regAccountId,
+ 'inviter_account_id' => $inviterId,
+ 'inviter_parent_account_id' => $inviterParentAid,
+ 'inviter_service_account_id' => $serviceId,
+ 'score' => $shareConf[self::SHARE_CURRENT] ?? 0,
+ 'parent_score' => $shareConf[self::SHARE_PARENT] ?? 0,
+ 'service_score' => $shareConf[self::SHARE_SERVICE] ?? 0,
+ 'is_active' => ShareRegLog::COMMON_OFF,
+ 'created_at' => date('Y-m-d H:i:s'),
+ ]);
+ return true;
+ }
+
+ /**
+ * 分享注册激活 【手机绑定时激活】
+ * 激活时:1、日志变更 2、积分发放【邀请人、邀请人上级、邀请人客服】 3、积分日志添加
+ *
+ * @param int $regAccountId
+ * @return bool
+ */
+ public function activeShareRegLog(int $regAccountId): bool
+ {
+ if ($regAccountId <= 0) {
+ return false;
+ }
+
+ Db::startTrans();
+ try {
+ if (!$regLog = ShareRegLog::where('reg_account_id', $regAccountId)->find()) {
+ return false;
+ }
+
+ if ($regLog['is_active'] == ShareRegLog::COMMON_ON) {
+ return true;
+ }
+
+ ShareRegLog::where('reg_account_id', $regAccountId)->save(
+ ['is_active' => ShareRegLog::COMMON_ON, 'activated_at' => date('Y-m-d H:i:s')]
+ );
+
+ $accountFields = ['id', 'score'];
+
+ // 邀请人积分添加
+ if ($regLog['score'] > 0) {
+ $inviter = Account::findById($regLog['inviter_account_id'], $accountFields);
+
+ AccountDataLog::log($regLog['inviter_account_id'], '分享注册', $regLog['score'],
+ AccountDataLog::TYPE_SCORE, AccountDataLog::ACTION_SHARE_REG,
+ $inviter['score'], '系统自动发放');
+ $inviter->save(
+ ['score' => Db::raw('`score` + '.$regLog['score'])]
+ );
+ }
+
+ // 邀请人父级积分添加
+ if ($regLog['inviter_parent_account_id'] > 0 && $regLog['parent_score'] > 0) {
+ $inviterParent = Account::findById($regLog['inviter_parent_account_id'], $accountFields);
+
+ AccountDataLog::log($regLog['inviter_parent_account_id'], '分享注册-下级分享', $regLog['parent_score'],
+ AccountDataLog::TYPE_SCORE, AccountDataLog::ACTION_SHARE_REG_CHILD,
+ $inviterParent['score'], '系统自动发放');
+ $inviterParent->save(
+ ['score' => Db::raw('`score` + '.$regLog['parent_score'])]
+ );
+ }
+
+ // 邀请人的客服积分添加
+ if ($regLog['inviter_service_account_id'] > 0 && $regLog['service_score']) {
+ $inviterService = Account::findById($regLog['inviter_service_account_id'], $accountFields);
+
+ AccountDataLog::log($regLog['inviter_service_account_id'], '分享注册-分享人客服获得积分', $regLog['service_score'],
+ AccountDataLog::TYPE_SCORE, AccountDataLog::ACTION_SHARE_REG_SERVICE,
+ $inviterService['score'], '系统自动发放');
+ $inviterService->save(
+ ['score' => Db::raw('`score` + '.$regLog['service_score'])]
+ );
+ }
+
+ Db::commit();
+ } catch (Exception $e) {
+ self::log($e);
+ Db::rollback();
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * 查看分享绑定的用户信息
+ *
+ * @param int $accountId
+ * @param string $grade
+ * @param int $page
+ * @param int $size
+ * @return array|null
+ */
+ public function shareUsers(int $accountId, string $grade, int $page = 1, int $size = 10): ?array
+ {
+ $data = [
+ 'total' => 0,
+ 'current' => $page,
+ 'size' => $size,
+ 'list' => new Collection(),
+ ];
+
+ try {
+ if (!in_array($grade, [self::SHARE_GRADE_FIRST, self::SHARE_GRADE_SECOND])) {
+ throw new RepositoryException('层级参数错误');
+ }
+
+ $fields = ['id', 'real_name', 'nickname', 'headimgurl', 'invite_source', 'phone_active'];
+ $whereMap = [];
+
+ switch ($grade) {
+ case self::SHARE_GRADE_FIRST:
+ $whereMap[] = ['inviter_account_id', '=', $accountId];
+ break;
+ case self::SHARE_GRADE_SECOND:
+ $whereMap[] = ['inviter_parent_account_id', '=', $accountId];
+ break;
+ }
+ $whereMap[] = ['is_active', '=', self::BOOL_TRUE];
+ $orders = ['id' => 'desc'];
+
+ $data = ShareRegLog::findList($whereMap, [], $page, $size, function ($q) use ($fields) {
+ return $q->with([
+ 'account' => function ($q2) use ($fields) {
+ $q2->field($fields);
+ }
+ ]);
+ }, $orders);
+
+ $inviteSourceTextList = self::inviteSourceList();
+ foreach ($data['list'] as $item) {
+ $item['desc'] = '';
+ $item['grade'] = $grade;
+
+ if ($grade == self::SHARE_GRADE_SECOND) {
+ $item['score'] = $item['parent_score'];
+ }
+
+ if ($grade == self::SHARE_GRADE_SERVICE) {
+ $item['score'] = $item['service_score'];
+ }
+
+ unset($item['parent_score']);
+ unset($item['service_score']);
+
+ $regAccount = $item['account'];
+ if ($regAccount) {
+ $regAccount['invite_source_desc'] = '';
+ if ($regAccount['invite_source'] != self::INVITE_SOURCE_DEF) {
+ $regAccount['invite_source_desc'] = $inviteSourceTextList[$regAccount['invite_source']] ?? '';
+ }
+ $regAccount['headimgurl'] = File::convertCompleteFileUrl($regAccount['headimgurl']);
+ }
+ $item['account'] = $regAccount;
+ }
+
+ } catch (RepositoryException | Exception $e) {
+
+ }
+
+ return $data;
+ }
+
+ /**
+ * 用户行为记录统计
+ * 点赞、收藏、分享等
+ * @param string $type
+ * @param string $action
+ * @param int $accountId
+ * @return int
+ */
+ public function countRecordByAction(string $type, string $action, int $accountId = 0): int
+ {
+ if (!in_array($type, AccountRecord::allowTypes())) {
+ return 0;
+ }
+
+ if (!in_array($action, AccountRecord::allowActions())) {
+ return 0;
+ }
+
+ return AccountRecord::countByAction($type, $action, $accountId);
+ }
+
+ /**
+ * 获取用户绑定的客服人员Id
+ */
+ public function getBoundServiceAId(int $accountId)
+ {
+ try {
+ return CustomerReceive::getBoundService($accountId);
+ } catch (Exception $e) {
+ return 0;
+ }
+ }
+
+ /**
+ * 获取用户绑定的客服人员
+ */
+ public function getBoundService(int $customerAId, string $action)
+ {
+ try {
+ return CustomerReceive::where('customer_aid', $customerAId)
+ ->where('action', $action)
+ ->find();
+ } catch (Exception $e) {
+ return null;
+ }
+ }
+
+ /**
+ * 获取客户接待流转记录
+ *
+ * @param int $customerAId
+ * @param array $actions
+ * @return CustomerReceive[]|array|Collection
+ */
+ public function findListCustomerReceive(int $customerAId, array $actions)
+ {
+ try {
+ return CustomerReceive::where('customer_aid', $customerAId)
+ ->whereIn('action', $actions)
+ ->select();
+ } catch (Exception $e) {
+ return new Collection();
+ }
+ }
+
+ // 足迹列表
+ public function footmarks(int $accountId, int $customerId = 0, string $keyword = '', int $page = 1, int $size = 20): array
+ {
+ $customerIds = [];
+ if (!$account = Account::findById($accountId)) {
+ throw new RepositoryException('账号信息不存在');
+ }
+ if (!$account->is_staff) {
+ throw new RepositoryException('您不是员工');
+ }
+
+ $exclude = $accountId == $customerId ? 0 : $accountId;
+
+ $rules = AccountRepository::getInstance()->getAccountRules($accountId);
+ $ruleChildren = in_array(AccountRule::RULE_FOOTMARKS, $rules);// 查看下级客户
+ $ruleAll = in_array(AccountRule::RULE_VIEW_ALL_FOOTMARKS, $rules); // 查看所有足迹
+
+ // 查看指定人足迹 检查该员工是否可查看
+ if (!$ruleChildren && !$ruleAll) {
+ throw new RepositoryException('权限不足');
+ }
+ if ($customerId > 0) {
+ $customerIds[] = $customerId;
+ }
+
+ if ($ruleAll) {
+ $accountId = 0;// 查看全部权限 则不指定员工account_id
+ }
+
+ return AccountFootmarks::fetch($accountId, $customerIds, $keyword, $page, $size, $exclude);
+ }
+
+ /**
+ * 客户列表
+ *
+ * @param array $where
+ * @param array $field
+ * @param int $accountId
+ * @param int $page
+ * @param int $size
+ * @param callable|null $call
+ * @return array
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ * @throws RepositoryException
+ */
+ public function customerList(array $where = [], array $field = [], int $accountId = 0, int $page = 1, int $size = 20, callable $call = null): array
+ {
+ if ($accountId > 0) {
+ if (!$staff = Staff::findOne(['account_id' => $accountId])) {
+ throw new RepositoryException('你不是员工');
+ }
+
+ $staffRules = explode(',', $staff['rules']);
+
+ if (!in_array('customer-list', $staffRules)) {
+ throw new RepositoryException('请获取客户列表查看权限');
+ }
+
+ // 是否具有查看所有用户权限
+ if (!in_array('view-all-customer-list', $staffRules)) {
+ // 查看自己的客户
+ // 获取指定用户的客户 TODO 此处若下级过多 会有性能影响
+ $customerIds = CustomerReceive::getChildrenIds($accountId);
+
+ $where[] = ['id', 'in', $customerIds];
+ }
+ }
+
+ return $this->getAndHandleAccountList($where, $field, $page, $size, $call);
+ }
+
+ /**
+ * 获取并处理用户列表 【后台用户列表】
+ *
+ * @param array $where
+ * @param array $field 必传字段coin_total
+ * @param int $page
+ * @param int $size
+ * @param callable|null $call
+ * @return array|null
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ * @throws RepositoryException
+ */
+ public function getAndHandleAccountList(array $where, array $field, int $page = 1, int $size = 20, callable $call = null): ?array
+ {
+ $items = self::findList($where, $field, $page, $size, function ($q) use ($call) {
+ if ($call !== null) {
+ $q = $call($q);
+ }
+ return $q
+ ->with(['tags', 'staff', 'customer', 'serviceList'])
+ ->order('id', 'desc');
+ });
+
+ $levelList = AccountRepository::getInstance()->getLevelList();
+ $items['list']->each(function ($item) use ($levelList) {
+ $item->channel_text = $this->model::channelTextList()[$item['channel']] ?? '未知来源';
+
+ $item->share_source = $item->is_staff > 0 ? ($item->staff->name ?? '') : ($item->customer->nickname ?? '');
+ $item->is_sign_text = $item->is_sign ? '是' : '否';
+ $item->tag = $item->tags->column('name');
+ $item->service = $item->serviceList->name ?? '';
+ $item->service_name = $item->serviceList->name ?? '';
+ $item->source_detail = AccountRepository::getInstance()->getSourceDetail($item->id);
+ $item->levelInfo = AccountRepository::getInstance()->getCurrentLevel($item->coin_total, $levelList);
+
+ $genderText = '保密';
+ if ($item->gender == 1) {
+ $genderText = '男';
+ }
+ if ($item->gender == 2) {
+ $genderText = '女';
+ }
+ $item->gender_text = $genderText;
+ });
+
+ return $items;
+ }
+
+ /**
+ * 获取账号详细来源
+ *
+ * @param int $accountId
+ * @return string
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ * @throws RepositoryException
+ */
+ public function getSourceDetail(int $accountId): string
+ {
+ if (!$account = $this->findById($accountId)) {
+ return '';
+ }
+
+ switch ($account['channel']) {
+ case AccountModel::CHANNEL_NORMAL:
+ return '自然流量进入';
+ case AccountModel::CHANNEL_MEMBER:
+ // 员工信息
+ if (!$source = Staff::findOne(['account_id' => $account['inviter_account_id']])) {
+ //员工不存在 查找用户信息
+ if (!$source = AccountModel::findById($account['inviter_account_id'])) {
+ //用户不存在
+ $member = '用户已不存在';
+ } else {
+ $member = $source['name'];
+ }
+ } else {
+ $member = $source['name'];
+ }
+ return sprintf("员工【%s】分享进入", $member);
+ case AccountModel::CHANNEL_CUSTOMER:
+ //客户不存在
+ $ddd = $account['inviter_account_id'];
+ if (!$source = AccountModel::findById($account['inviter_account_id'])) {
+ //用户不存在
+ $member = '客户已不存在';
+ } else {
+ $member = $source['nickname'].':'.$source['mobile'];
+ }
+ return sprintf("客户【%s】分享进入", $member);
+ case AccountModel::CHANNEL_ACTIVITY:
+ //活码
+ if (!$source = Activity::findOne(['code' => $account['source_code']])) {
+ //用户不存在
+ $member = '活码已不存在';
+ } else {
+ $member = $source['name'];
+ }
+ return sprintf("通过活码【%s】进入", $member);
+ default:
+ return AccountModel::channelTextList()[$account['channel']] ?? '未知渠道';
+ }
+ }
+
+ /**
+ * 个人海报模板列表
+ *
+ * @param int $accountId
+ * @param int $page
+ * @param int $size
+ * @return array
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ * @throws RepositoryException
+ */
+ public function poster(int $accountId, int $page = 1, int $size = 10): array
+ {
+ if (!$account = Account::findById($accountId)) {
+ throw new RepositoryException('请先登录');
+ }
+
+ $list = [];
+ // 获取可用的会员海报
+ $levelPoster = AccountLevel::where('value', '<', $account['coin_total'])->order('value', 'desc')->column('poster');
+
+ foreach ($levelPoster as $poster) {
+ $list = array_merge($list, explode(',', $poster));
+ }
+ // 获取基础海报列表
+ Config::load('extra/mini_program', 'mini_program');
+ $conf = config('mini_program');
+ $basePoster = $conf['poster'] ?? [];
+ $list = array_merge($list, $basePoster);
+ $list = array_filter($list);
+ return [
+ 'total' => count($list),
+ 'current' => $page,
+ 'size' => $size,
+ 'list' => array_slice($list, ($page - 1) * $size, $size),
+ ];
+ }
+
+ /**
+ * @throws RepositoryException
+ */
+ public function posterInfo(int $accountId, string $posterSrc, string $domain)
+ {
+ $bgImg = $posterSrc;
+ $bgImg = empty($bgImg) ? '' : File::convertCompleteFileUrl($bgImg);
+
+ try {
+ if (!$account = AccountRepository::getInstance()->findById($accountId)) {
+ throw new RepositoryException('请先登录');
+ }
+
+ $inviteCode = $account['invite_code'] ?: md5($account['id']);
+ $inviteUrl = $domain.'/share?invite_code='.$inviteCode;
+ $logoImg = app()->getRootPath().'public/static/images/icon-logo.jpg';
+
+ $result = Builder::create()
+ ->writer(new PngWriter())
+ ->writerOptions([])
+ ->data($inviteUrl)
+ ->encoding(new Encoding('UTF-8'))
+ ->errorCorrectionLevel(new ErrorCorrectionLevelHigh())
+ ->size(300)
+ ->margin(10)
+ ->roundBlockSizeMode(new RoundBlockSizeModeMargin())
+ ->logoPath($logoImg)
+ ->logoResizeToHeight(100)
+ ->logoResizeToWidth(100)
+ ->logoPunchoutBackground(true)
+ ->build();
+
+ $dataUri = $result->getDataUri();
+ return GdTool::generatePoster($dataUri, $bgImg);
+ } catch (Exception $e) {
+ AccountRepository::log('个人海报生成失败', $e);
+ throw $e;
+ }
+ }
+
+ /**
+ * 获取个人日记
+ *
+ * @param int $accountId
+ * @param int $page
+ * @param int $size
+ * @return array
+ * @throws Exception
+ */
+ public function diary(int $accountId, int $page = 1, int $size = 10): array
+ {
+ $where = [];
+ $where[] = ['publish_account', '=', $accountId];
+ // $where[] = ['diary_check', '=', Archives::COMMON_ON];
+
+ $field = [];
+ return Archives::findList($where, $field, $page, $size, null, ['sort' => 'desc', 'id' => 'desc']);
+ }
+
+ /**
+ * 日记添加
+ *
+ * @param array $params
+ * @return bool
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ * @throws RepositoryException
+ */
+ public function diarySave(array $params): bool
+ {
+ $id = $params['id'] ?? 0;
+ $now = date('Y-m-d H:i:s');
+
+ if ($id > 0) {
+ // 修改
+ $item = Archives::findById($id);
+ if ($item) {
+ $item->save($params);
+ } else {
+ $id = 0;
+ }
+ }
+
+ $doctorArr = [];
+ if (isset($params['doctor_id'])) {
+ $doctorArr = explode(',', $params['doctor_id']);
+ }
+
+ // 新建日记
+ if ($id <= 0) {
+ $params['category_id'] = ArchivesCategory::where('name', ArchivesCategory::NAME_DIARY)->value('id');
+ $params['created_at'] = $now;
+ $params['published_at'] = $now;
+ $params['created_by'] = $params['publish_account'];
+
+ $diaryCollection = $this->getDiaryCollection($params['publish_account']);
+ if (!$diaryCollection) {
+ $diaryCollection = $this->createDiaryCollection($params['publish_account'], $params['disease_id'], $doctorArr);
+ }
+
+ $params['diary_id'] = $diaryCollection['id'];
+
+ $archives = Archives::create($params);
+ $id = $archives['id'];
+ }
+
+ if ($doctorArr) {
+ //关联医生
+ DoctorRelation::associateDoctor($doctorArr, $id, DoctorRelation::TYPE_CONTENT);
+ }
+
+ return true;
+ }
+
+ /**
+ * 删除日记
+ *
+ * @param array $ids
+ * @return bool
+ */
+ public function diaryDel(array $ids): bool
+ {
+ Archives::whereIn('id', $ids)->delete();
+ // 删除关联医生
+ DoctorRelation::whereIn('relation_id', $ids)->where('type', DoctorRelation::TYPE_CONTENT)->delete();
+ return true;
+ }
+
+ /**
+ * 日记详情
+ *
+ * @param int $id
+ * @return array|Model|null
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ */
+ public function diaryInfo(int $id)
+ {
+ return Archives::findById($id);
+ }
+
+ /**
+ * 获取用户日记合集
+ *
+ * @param int $accountId
+ * @return array|Diary|Model|null
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ */
+ public function getDiaryCollection(int $accountId)
+ {
+ if (!$diary = Diary::where('account_id', $accountId)->find()) {
+ return null;
+ }
+ return $diary;
+ }
+
+ /**
+ * 创建日记合集
+ *
+ * @param int $accountId
+ * @param int $diseaseId
+ * @param array $doctorIds 关联医生
+ * @return Diary|Model
+ * @throws RepositoryException
+ */
+ public function createDiaryCollection(int $accountId, int $diseaseId, array $doctorIds = [])
+ {
+ if (!$account = AccountRepository::getInstance()->findById($accountId)) {
+ throw new RepositoryException('用户信息错误');
+ }
+
+ $now = date('Y-m-d H:i:s');
+ $diary = Diary::create([
+ 'title' => '日记合集-'.$account['nickname'],
+ 'disease_id' => $diseaseId,
+ 'user' => $account['nickname'],
+ 'headimg' => $account['headimgurl'],
+ 'created_at' => $now,
+ 'published_at' => $now,
+ 'account_id' => $accountId,
+ ]);
+
+ if ($doctorIds) {
+ DoctorRelation::associateDoctor($doctorIds, $diary['id'], DoctorRelation::TYPE_DIARY);
+ }
+ return $diary;
+ }
+
+
+ /**
+ * 创建消息 支持推送订阅消息和短信 支持批量
+ *
+ * @param array $item
+ * item[title]: 是生生世世 消息标题
+ * item[type]: notice 消息类型 此处固定为notice 通知
+ * item[target]: part 目标类型 all=所有人 part=部分人
+ * item[subscribe_temp_id]: d0efR-Ga27c6eIvx9mAwJcnAqzhM_Sq68XiFvjvlBJM 订阅消息模版ID
+ * item[subscribe_data][thing1]: 事实上 订阅消息内容 subscribe_data根据模版变动参数
+ * item[subscribe_data][time4]: 坎坎坷坷
+ * item[subscribe_data][thing5]: 哈对方的身份
+ * item[sms_temp_id]: SMS_231436568 短信模版ID
+ * item[content]: 收拾收拾 通知内容
+ *
+ * @param array $targetList [13,15] 发送人ID列表
+ * @throws GuzzleException
+ */
+ public function createMessage(array $item, array $targetList)
+ {
+ $repo = AccountRepository::getInstance();
+
+ try {
+ $type = $item['type'] ?? '';
+ $target = $item['target'] ?? '';
+
+ $subscribeData = $item['subscribe_data'] ?? [];
+ $smsData = $item['sms_data'] ?? [];
+
+ $item["send_at"] = date('Y-m-d H:i:s');
+ $item["content"] = $item['content'];
+
+ $item['is_push'] = MessageModel::COMMON_ON;
+ $item['to_subscribe'] = (int) !empty($item['subscribe_temp_id']);
+ $item['to_sms'] = (int) !empty($item['sms_temp_id']);
+
+ $item['subscribe_data'] = json_encode($subscribeData, JSON_UNESCAPED_UNICODE);
+ $item['sms_data'] = json_encode($smsData, JSON_UNESCAPED_UNICODE);
+ $message = $repo->addMessage($type, $target, $targetList, $item);
+ } catch (Exception $e) {
+ AccountRepository::log('创建消息通知失败 ', $e);
+ }
+
+ try {
+ if ($item['to_sms']) {
+ // 批量发送短信
+ $res = AccountRepository::getInstance()->smsSend($message->id, $item['sms_temp_id'], $targetList, $smsData);
+ // var_dump($res);
+ }
+ } catch (Exception $e) {
+ AccountRepository::log('短信发送失败 ', $e);
+ }
+
+ try {
+ if ($item['to_subscribe'] > 0) {
+ // 订阅消息发送
+ AccountRepository::getInstance()->subscribeSend($message->id, $item['subscribe_temp_id'],
+ $targetList, $subscribeData,
+ \app\model\Config::MINI_PATH_MESSAGE_CENTER);
+ }
+ } catch (Exception $e) {
+ AccountRepository::log('订阅消息发送失败 ', $e);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/repository/ArchivesRepository.php b/app/repository/ArchivesRepository.php
new file mode 100644
index 0000000..8b3cb26
--- /dev/null
+++ b/app/repository/ArchivesRepository.php
@@ -0,0 +1,33 @@
+value('id');
+ if ($categoryId) {
+ return Block::getByCategoryId($categoryId);
+ }
+ return [];
+ }
+}
\ No newline at end of file
diff --git a/app/repository/CmsRepository.php b/app/repository/CmsRepository.php
new file mode 100644
index 0000000..69140e5
--- /dev/null
+++ b/app/repository/CmsRepository.php
@@ -0,0 +1,75 @@
+ 'aa', 'value' => 1, 'selected' => true, 'prefix' => ' ']]
+ *
+ * @param array $data 待处理的数据
+ * @param string $symbol 分隔符号 默认
+ * @param int $repeatNum 重复次数 默认4
+ * @return array
+ */
+ public function handleSelectedList(array $data, string $symbol = ' ', int $repeatNum = 4): array
+ {
+ $list = [];
+ foreach ($data as $item) {
+ $level = $item['level'] ?? 0;
+ $arr = $item;
+ $arr['children'] = $arr['children'] ?? [];
+ $arr['prefix'] = str_repeat($symbol, $level * $repeatNum);
+ $list[] = $arr;
+ }
+
+ return $list;
+ }
+
+ /**
+ * 获取后台用户权限列表
+ *
+ * @param int $accountId
+ * @return array
+ */
+ public function getUserRules(int $accountId): array
+ {
+ $rules = [];
+ $roles = Enforcer::getRolesForUser($accountId);
+ foreach ($roles as $role) {
+ $rules = array_merge($rules, Enforcer::getPermissionsForUser($role));
+ }
+
+ $ruleNameList = [];
+ foreach ($rules as $rule) {
+ if (isset($rule[2])) {
+ $ruleNameList[] = $rule[1].':'.$rule[2];
+ } else {
+ $ruleNameList[] = $rule[1];
+ }
+ }
+
+ return array_unique($ruleNameList);
+ }
+}
\ No newline at end of file
diff --git a/app/repository/CommonRepository.php b/app/repository/CommonRepository.php
new file mode 100644
index 0000000..77cd60d
--- /dev/null
+++ b/app/repository/CommonRepository.php
@@ -0,0 +1,112 @@
+ $phone,
+ 'type' => $type,
+ 'code' => rand(100000, 999999),
+ 'created_at' => date('Y-m-d H:i:s', $now),
+ 'expired_at' => date('Y-m-d H:i:s', $now + 5 * 60),
+ ]);
+
+ $res = Sms::send($phone, $res['code']);
+ if (is_bool($res)) {
+ return $res;
+ }
+
+ Log::error(json_encode($res, JSON_FORCE_OBJECT));
+ return false;
+ } catch (Exception $e) {
+ Log::error(sprintf("[发送短信验证码失败]%s:%d %s", $e->getFile(), $e->getLine(), $e->getMessage()));
+ return false;
+ }
+ }
+
+ /**
+ * 检查短信验证码
+ *
+ * @param string $phone
+ * @param string $code
+ * @param string $type
+ * @return bool
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ */
+ public function checkSms(string $phone, string $code, string $type): bool
+ {
+ $item = (new SmsLog())->where('phone', $phone)
+ ->where('type', $type)
+ ->where('code', $code)
+ ->order('created_at', 'desc')
+ ->order('id', 'desc')
+ ->find();
+ if (!$item) {
+ return false;
+ }
+
+ if ($item['expired_at'] < date('Y-m-d H:i:s')) {
+ return false;
+ }
+
+ return $item->save(['status' => self::SMS_STATUS_OK]);
+ }
+
+ /**
+ * 日志记录
+ *
+ * @param string $msg
+ * @param Exception|null $e
+ * @param string $level
+ * @param string $channel
+ */
+ public static function log(string $msg, Exception $e = null, string $level = 'error', string $channel = 'file')
+ {
+ if ($e != null) {
+ $msg = sprintf("[%s]%s:%s %s", $msg, $e->getFile(), $e->getLine(), $e->getMessage());
+ } else {
+ $msg = sprintf("%s", $msg);
+
+ }
+ Log::channel($channel)->$level($msg);
+ }
+}
\ No newline at end of file
diff --git a/app/repository/OperationRepository.php b/app/repository/OperationRepository.php
new file mode 100644
index 0000000..47311e3
--- /dev/null
+++ b/app/repository/OperationRepository.php
@@ -0,0 +1,212 @@
+toArray();
+ } catch (Exception $e) {
+ return [];
+ }
+ }
+
+ /**
+ * 获取轮播
+ *
+ * @param int $id
+ * @return array|Model|null
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ */
+ public function findSlideById(int $id)
+ {
+ return Slide::findById($id);
+ }
+
+ /**
+ * 轮播列表
+ *
+ * @param array $where
+ * @param array $fields
+ * @param int $page
+ * @param int $size
+ * @param callable|null $call
+ * @param array $orders
+ * @return array
+ * @throws Exception
+ */
+ public function slideList(array $where=[], array $fields=[], int $page=1, int $size=20, callable $call=null, array $orders=[]): array
+ {
+ return Slide::findList($where, $fields, $page, $size, $call, $orders);
+ }
+
+ /**
+ * 更新轮播
+ *
+ * @param array $data
+ * @param int $id
+ * @return bool
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ */
+ public function updateSlide(array $data, int $id): bool
+ {
+ $item = (new Slide())->where('id', $id)->find();
+ if ($item) {
+ return $item->save($data);
+ }
+ return false;
+ }
+
+ /**
+ * 创建轮播
+ *
+ * @param array $data
+ * @return Slide
+ */
+ public function createSlide(array $data): Slide
+ {
+ $data['created_at'] = date('y-m-d H:i:s');
+ return Slide::create($data);
+ }
+
+ /**
+ * 删除轮播图
+ *
+ * @param array $ids
+ * @return bool
+ */
+ public function deleteSlides(array $ids): bool
+ {
+ return Slide::deleteByIds($ids);
+ }
+
+ /**
+ * 轮播位置是否存在
+ *
+ * @param string $position
+ * @param int $exceptId 需要排除的显示位置ID
+ * @return bool
+ */
+ public function slidePositionExists(string $position, int $exceptId = 0): bool
+ {
+ return (new SlidePosition())->when($exceptId > 0, function ($q) use ($exceptId) {
+ $q->where('id', '<>', $exceptId);
+ })->where('key', $position)->count() > 0;
+ }
+
+ /**
+ * 根据显示位置查询相关的轮播图信息
+ *
+ * @param string $position 轮播图位置标识
+ * @param int $size 限制查询数量
+ * @throws Exception
+ */
+ public function slideListByPosition(string $position, int $size=0): ?Collection
+ {
+ $where[] = ['position', '=', $position];
+ $orders = ['sort'=>'asc'];
+ return Slide::findList($where, [], 1, $size, null, $orders)['list'];
+ }
+
+
+ /**
+ * 轮播显示位置列表
+ *
+ * @param array $where
+ * @param array $fields
+ * @param int $page
+ * @param int $size
+ * @param callable|null $call
+ * @param array $orders
+ * @return array
+ * @throws Exception
+ */
+ public function slidePositionList(array $where=[], array $fields=[], int $page=1, int $size=20, callable $call=null, array $orders=[]): array
+ {
+ return SlidePosition::findList($where, $fields, $page, $size, $call, $orders);
+ }
+
+ /**
+ * 更新轮播位置
+ *
+ * @param array $data
+ * @param int $id
+ * @return bool
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ */
+ public function updateSlidePosition(array $data, int $id): bool
+ {
+ $item = (new SlidePosition())->where('id', $id)->find();
+ if ($item) {
+ $item->save($data);
+ }
+ return false;
+ }
+
+ /**
+ * 创建轮播位置
+ *
+ * @param array $data
+ * @return SlidePosition
+ */
+ public function createSlidePosition(array $data): SlidePosition
+ {
+ $data['created_at'] = date('y-m-d H:i:s');
+ return SlidePosition::create($data);
+ }
+
+ /**
+ * 删除轮播位置
+ *
+ * @param array $ids
+ * @return bool
+ */
+ public function deleteSlidePositions(array $ids): bool
+ {
+ return SlidePosition::deleteByIds($ids);
+ }
+
+ /**
+ * 获取轮播位置
+ *
+ * @param int $id
+ * @return array|Model|null
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ */
+ public function findSlidePositionById(int $id)
+ {
+ return SlidePosition::findById($id);
+ }
+
+}
\ No newline at end of file
diff --git a/app/service.php b/app/service.php
new file mode 100644
index 0000000..8c2052c
--- /dev/null
+++ b/app/service.php
@@ -0,0 +1,9 @@
+getMessage());
+ return null;
+ }
+ }
+ return self::$oss;
+ }
+}
\ No newline at end of file
diff --git a/app/service/Alipay.php b/app/service/Alipay.php
new file mode 100644
index 0000000..ff0b4aa
--- /dev/null
+++ b/app/service/Alipay.php
@@ -0,0 +1,64 @@
+ trim($conf['appId']),
+ 'notify_url' => trim($conf['notify_url']),
+ 'return_url' => trim($conf['return_url']) ?? trim($conf['notify_url']),
+ 'ali_public_key' => trim($conf['aliPubKey']),//注意 这里的是支付宝的公钥
+ // 加密方式: **RSA2**
+ 'private_key' => trim($conf['priKey']),
+ // 使用公钥证书模式,请配置下面两个参数,同时修改ali_public_key为以.crt结尾的支付宝公钥证书路径,
+ // 如(./cert/alipayCertPublicKey_RSA2.crt)
+ // 'app_cert_public_key' => './cert/appCertPublicKey.crt', //应用公钥证书路径
+ // 'alipay_root_cert' => './cert/alipayRootCert.crt', //支付宝根证书路径
+ 'log' => [ // optional
+ 'file' => './logs/alipay.log',
+ 'level' => 'debug', // 建议生产环境等级调整为 info,开发环境为 debug
+ 'type' => 'single', // optional, 可选 daily.
+ 'max_file' => 30, // optional, 当 type 为 daily 时有效,默认 30 天
+ ],
+ 'http' => [ // optional
+ 'timeout' => 5.0,
+ 'connect_timeout' => 5.0,
+ // 更多配置项请参考 [Guzzle](https://guzzle-cn.readthedocs.io/zh_CN/latest/request-options.html)
+ ],
+ // 'mode' => 'dev', // optional,设置此参数,将进入沙箱模式
+ ];
+ }
+
+ //支付宝支付实例 单例模式
+ public static function getInstance(): ?\Yansongda\Pay\Gateways\Alipay
+ {
+ if (self::$app == null) {
+ self::$app = Pay::alipay(self::config());
+ }
+ return self::$app;
+ }
+}
\ No newline at end of file
diff --git a/app/service/DxtcPage.php b/app/service/DxtcPage.php
new file mode 100644
index 0000000..9a5a405
--- /dev/null
+++ b/app/service/DxtcPage.php
@@ -0,0 +1,204 @@
+currentPage() <= 1) {
+ return ('上一页');
+ }
+
+ $url = $this->url(
+ $this->currentPage() - 1
+ );
+
+// return ($this->getPageLinkWrapper($url, "上一页" ,"") .$this->getPageLinkWrapper($url, $text ,"prev"));
+ return ($this->getPageLinkWrapper($url, "上一页" ,"prev"));
+ }
+
+ /**
+ * 下一页按钮
+ * @param string $text
+ * @return string
+ */
+ protected function getNextButton(string $text = '>'): string
+ {
+ if (!$this->hasMore) {
+ //return ('');
+ return ('下一页');
+ }
+
+ $url = $this->url($this->currentPage() + 1);
+
+ return ($this->getPageLinkWrapper($url, "下一页",'next')) ;
+ }
+
+ /**
+ * 页码按钮
+ * @return string
+ */
+ protected function getLinks(): string
+ {
+ if ($this->simple) {
+ return '';
+ }
+
+ $block = [
+ 'first' => null,
+ 'slider' => null,
+ 'last' => null,
+ ];
+
+ $side = 3;
+ $window = $side * 1;
+
+ if ($this->lastPage < $window + 3) {
+ $block['first'] = $this->getUrlRange(1, $this->lastPage);
+ } elseif ($this->currentPage <= $window) {
+ $block['first'] = $this->getUrlRange(1, $window );
+ $block['last'] = $this->getUrlRange($this->lastPage - 1, $this->lastPage);
+ } elseif ($this->currentPage > ($this->lastPage - $window)) {
+ $block['first'] = $this->getUrlRange(1, 2);
+ $block['last'] = $this->getUrlRange($this->lastPage - ($window ), $this->lastPage);
+ } else {
+ $block['first'] = $this->getUrlRange(1, 2);
+ $block['slider'] = $this->getUrlRange($this->currentPage , $this->currentPage );
+ $block['last'] = $this->getUrlRange($this->lastPage - 1, $this->lastPage);
+ }
+
+ $html = '';
+
+ if (is_array($block['first'])) {
+ $html .= $this->getUrlLinks($block['first']);
+ }
+
+ if (is_array($block['slider'])) {
+ $html .= $this->getDots();
+ $html .= $this->getUrlLinks($block['slider']);
+ }
+
+ if (is_array($block['last'])) {
+ $html .= $this->getDots();
+ $html .= $this->getUrlLinks($block['last']);
+ }
+
+ return $html;
+ }
+
+ /**
+ * 渲染分页html
+ * @return mixed
+ */
+ public function render($linkStr='')
+ {
+ $this->linkStr=$linkStr;
+ if ($this->hasPages()) {
+ if ($this->simple) {
+ return sprintf(
+ '%s %s
',
+ $this->getPreviousButton(),
+ $this->getNextButton()
+ );
+ } else {
+ return sprintf(
+ '%s %s %s
',
+ $this->getPreviousButton(),
+ $this->getLinks(),
+ $this->getNextButton()
+ );
+ }
+ }
+ }
+
+ /**
+ * 生成一个可点击的按钮
+ *
+ * @param string $url
+ * @param string $page
+ * @return string
+ */
+ protected function getAvailablePageWrapper(string $url, string $page,$class=""): string
+ {
+ //return '' . $page . '';
+ return '' . $page . '';
+ }
+
+ /**
+ * 生成一个禁用的按钮
+ *
+ * @param string $text
+ * @return string
+ */
+ protected function getDisabledTextWrapper(string $text,$class=""): string
+ {
+ //return '' . $text . ' ';
+ return '' . $text . '';
+ }
+
+ /**
+ * 生成一个激活的按钮
+ *
+ * @param string $text
+ * @return string
+ */
+ protected function getActivePageWrapper(string $text,$class=""): string
+ {
+ //return '' . $text . '';
+ return '' . $text . '';
+ }
+
+ /**
+ * 生成省略号按钮
+ *
+ * @return string
+ */
+ protected function getDots(): string
+ {
+ return $this->getDisabledTextWrapper('...');
+ }
+
+ /**
+ * 批量生成页码按钮.
+ *
+ * @param array $urls
+ * @return string
+ */
+ protected function getUrlLinks(array $urls): string
+ {
+ $html = '';
+
+ foreach ($urls as $page => $url) {
+ $html .= $this->getPageLinkWrapper($url, $page);
+ }
+
+ return $html;
+ }
+
+ /**
+ * 生成普通页码按钮
+ *
+ * @param string $url
+ * @param string $page
+ * @return string
+ */
+ protected function getPageLinkWrapper(string $url, string $page ,$class=""): string
+ {
+ if ($this->currentPage() == $page) {
+ return $this->getActivePageWrapper($page,'active');
+ }
+
+ return $this->getAvailablePageWrapper($url, $page,$class);
+ }
+}
diff --git a/app/service/Excel.php b/app/service/Excel.php
new file mode 100644
index 0000000..8a1c7ab
--- /dev/null
+++ b/app/service/Excel.php
@@ -0,0 +1,201 @@
+ [
+ 'name' => '宋体',
+ ],
+ 'alignment' => [
+ 'horizontal' => Alignment::HORIZONTAL_CENTER, // 水平居中
+ 'vertical' => Alignment::VERTICAL_CENTER, // 垂直居中
+ 'wrapText' => true,
+ ],
+ 'borders' => [
+ 'allBorders' => [
+ 'borderStyle' => Border::BORDER_THIN,
+ 'color' => ['rgb' => 'eeeeee'],
+ ]
+ ],
+ ];
+
+ public static array $defaultSetting = [
+ 'cell_width' => 30, // 默认列宽
+ 'font_size' => 12, // 默认excel内容字体大小
+ ];
+
+ //导出
+ static public function export($spreadsheet,$filename)
+ {
+ $writer = new Xlsx($spreadsheet);
+ header("Content-Type: application/force-download");
+ header("Content-Type: application/octet-stream");
+ header("Content-Type: application/download");
+ header('Content-Disposition:inline;filename="'.$filename.'"');
+ header("Content-Transfer-Encoding: binary");
+ header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
+ header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
+ header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
+ header("Pragma: no-cache");
+ $writer->save('php://output');
+ $spreadsheet->disconnectWorksheets();
+ unset($spreadsheet);
+ exit;
+ }
+
+ public static function cancelTimeLimit()
+ {
+ ini_set('max_execution_time', '0');
+ ini_set("memory_limit", '-1');
+ set_time_limit(0);
+ }
+
+ /**
+ * 根据header字段获取可配置列的cellId列表
+ * 默认前26列为可配置列A~Z, $headerLength不能超过26 * 27 = 702
+ *
+ * @param int $headerLength
+ * @return array
+ */
+ public static function getCellIds(int $headerLength): array
+ {
+ $defaultCellIds = range('A', 'Z', 1);
+ $cellIds = $defaultCellIds;
+ $loop = ceil($headerLength / 26);
+ if($loop>1) {
+ $maxPrefixIndex = ($loop - 2) >= 25 ? 25 : ($loop - 2);
+ for ($prefixIndex = 0; $prefixIndex<= $maxPrefixIndex; $prefixIndex++) {
+ $prefix = $defaultCellIds[$prefixIndex];
+ $cellIds = array_merge($cellIds, array_map(function ($val) use($prefix) {
+ return $prefix.$val;
+ }, $defaultCellIds));
+ }
+ }
+ return $cellIds;
+ }
+
+ /**
+ * 设置导出表数据
+ *
+ * @param Worksheet $sheet 工作表对象
+ * @param array $cellValues 数据信息,二维数组(ps:若字段值需要指定样式以数组格式传递,如[$val, DataType::TYPE_STRING])。第二行开始填充
+ * @param array $header 表头信息, 第一行为表头
+ * @param string $sheetTitle 工作表标题
+ * @param array $cellWidthList 列宽样式,键值对,键名为列序号(从0开始计算)
+ * @param array $excelStyle 表数据样式
+ * @param array $defaultList 默认设置样式
+ * @param array $cellWrapList 列换行样式
+ * @return bool
+ */
+ public static function setExcelCells(Worksheet $sheet, array $cellValues, array $header, string $sheetTitle = '数据列表', $cellWidthList = [], $excelStyle = [], $defaultList = [], $cellWrapList = []): bool
+ {
+ $defaultStyle = [
+ 'font' => [
+ 'name' => '宋体',
+ ],
+ 'alignment' => [
+ 'horizontal' => Alignment::HORIZONTAL_CENTER, // 水平居中
+ 'vertical' => Alignment::VERTICAL_CENTER, // 垂直居中
+ 'wrapText' => false,
+ ],
+ 'borders' => [
+ 'allBorders' => [
+ 'borderStyle' => Border::BORDER_THIN,
+ 'color' => ['rgb'=>'eeeeee'],
+ ]
+ ],
+ ];
+ $headerLength = count($header);
+
+ if($headerLength === 0) return false;
+ $cellIds = self::getCellIds($headerLength);
+ $lastCellId = $cellIds[$headerLength-1];
+ $contentIndex = 2;
+ foreach ($cellValues as $n => $itemCell) {
+ foreach ($itemCell as $i => $cellValue) {
+ if(is_array($cellValue)) {
+ $sheet->setCellValueExplicit($cellIds[$i] . $contentIndex, ...$cellValue);
+ } else {
+ $sheet->setCellValue($cellIds[$i] . $contentIndex, $cellValue);
+ }
+ }
+ $contentIndex++;
+ }
+
+ try {
+ $headerStr = 'A1:' . $lastCellId.'1';
+ $bodyStr = 'A2:' . $lastCellId . ($contentIndex - 1);
+
+ $defaultWidth = 24; // 默认列宽24个字符
+ $fontSize = 12; // 默认字体大小为12号
+ if(!empty($defaultList)) {
+ if(isset($defaultList['cell_width']) && is_numeric($defaultList['cell_width']) && $defaultList['cell_width'] > 0) {
+ $defaultWidth = $defaultList['cell_width'];
+ }
+ if(isset($defaultList['font_size']) && is_numeric($defaultList['font_size']) && $defaultList['font_size'] > 0) {
+ $fontSize = $defaultList['font_size'];
+ }
+ }
+
+
+ $sheet->setTitle(empty($sheetTitle) ? '数据列表': $sheetTitle);
+ $sheet->getDefaultColumnDimension()->setAutoSize($fontSize);
+ $sheet->getStyle($headerStr)->getFont()->setBold(false)->setSize($fontSize+2);
+ $sheet->getDefaultColumnDimension()->setWidth($defaultWidth);
+ $sheet->fromArray($header, null, 'A1');
+ $sheet->getStyle($headerStr)->applyFromArray($defaultStyle);
+ $sheet->getStyle($bodyStr)->applyFromArray(empty($excelStyle) ? $defaultStyle : $excelStyle);
+
+ // 自定义列宽
+ if(!empty($cellWidthList)) {
+ foreach ($cellWidthList as $cellId => $widthVal) {
+ if(isset($cellIds[$cellId]) && is_numeric($widthVal) && $widthVal > 0) {
+ $sheet->getColumnDimension($cellIds[$cellId])->setWidth($widthVal);
+ }
+ }
+ }
+
+ //自定义列是否换行
+ if(!empty($cellWrapList)) {
+ foreach ($cellWrapList as $cellId => $boolVal) {
+ if(isset($cellIds[$cellId])) {
+ $wrap = $boolVal ? true : false;
+ $sheet->getStyle($cellIds[$cellId])->getAlignment()->setWrapText($wrap);
+ }
+ }
+ }
+
+ return true;
+ } catch (\Exception $e) {
+ return false;
+ }
+ }
+
+ // excel导入时间转换
+ public static function getExcelTime($timeStr, $format = 'Y-m-d')
+ {
+ $timezone = ini_get('date.timezone');
+ $timeStr = trim($timeStr);
+ if (!empty($timeStr)) {
+ if (is_numeric($timeStr)) {
+ $toTimestamp = EDate::excelToTimestamp($timeStr, $timezone);
+ $timeStr = date($format, $toTimestamp);
+ } else {
+ $toTimestamp = strtotime($timeStr);
+ $timeStr = ($toTimestamp === false) ? '' : date($format, $toTimestamp);
+ }
+ } else {
+ $timeStr = '';
+ }
+ return $timeStr;
+ }
+}
\ No newline at end of file
diff --git a/app/service/ExtraConfig.php b/app/service/ExtraConfig.php
new file mode 100644
index 0000000..53dac3d
--- /dev/null
+++ b/app/service/ExtraConfig.php
@@ -0,0 +1,62 @@
+getRootPath() . $upload_path;
+ $filename = uniqid() . '.' . $file->extension();
+ $upload_filename = '/' . $upload_path . '/' . $filename;
+ return [$file->move($path, $filename), $file, $upload_filename];
+ }
+
+ /**
+ * 文件访问路径转换为完整的url
+ * @param string|null $fileUrl
+ * @param bool $ossAnalysis 是否进行OSS解析
+ * @return string
+ * @todo 若启用OOS存储,需根据业务配置调整$fileDomain
+ *
+ */
+ public static function convertCompleteFileUrl(?string $fileUrl, bool $ossAnalysis=true): string
+ {
+ if (empty($fileUrl)) {
+ return '';
+ }
+ if ($ossAnalysis) {
+ $fileDomain = self::getFileDomain();
+ } else {
+ $fileDomain = request()->domain();
+ }
+ $prefix = substr($fileUrl, 0, 4);
+ if (!($prefix == 'http')) {
+ $fileUrl = $fileDomain.'/'.ltrim($fileUrl, '/');
+ }
+
+ return $fileUrl;
+ }
+
+ /**
+ * 文件访问域名前缀
+ *
+ * @return string
+ */
+ public static function getFileDomain(): string
+ {
+ $confBase = ExtraConfig::base();
+ $confOss = ExtraConfig::aliOss();
+ $isOss = $confBase['oss'] ?? 'false';
+ $ossDomain = $confOss['customDomain'] ?? '';
+
+ // 默认为当前域名
+ $fileDomain = request()->domain();
+ if ($isOss == 'true' && !empty($ossDomain)) {
+ $fileDomain = $ossDomain;
+ }
+
+ $fileDomain = trim($fileDomain);
+ return rtrim($fileDomain, '/');
+ }
+}
\ No newline at end of file
diff --git a/app/service/GdTool.php b/app/service/GdTool.php
new file mode 100644
index 0000000..35c9bef
--- /dev/null
+++ b/app/service/GdTool.php
@@ -0,0 +1,140 @@
+getRootPath().'public/static/images/poster-bg1.png';
+ $srcBga = empty($bgImg) ? $defBga : $bgImg;
+ $bgInfo = @getimagesize($srcBga);
+ if ($bgInfo[0] != 750 || $bgInfo[1] != 1334) {
+ throw new \Exception('海报模板尺寸不正确!');
+ }
+
+ if (!$bgInfo) {
+ throw new \Exception('海报背景图资源不存在!');
+ }
+
+ $ext = $bgInfo['mime'];
+ $extName = '';
+ $img = null;
+ switch ($ext) {
+ case 'image/jpeg':
+ $img = @imagecreatefromjpeg($srcBga);
+ $extName = 'jpg';
+ break;
+ case 'image/png':
+ $img = @imagecreatefrompng($srcBga);
+ $extName = 'png';
+ break;
+ }
+ if (!$img) {
+ throw new \Exception('无效背景图');
+ }
+
+ //2、准备颜色
+ $black = imagecolorallocate($img,0,0,0);
+ $while = imagecolorallocate($img,255,255,255);
+ $faColor = imagecolorallocate($img,0,104,51);
+
+ // 邀请人头像
+// $headimgurl = 'https://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTJXE3Zz0U5edXYI2icicYibSNwwezWe0X92fovRtpUwdCF5lAmjsYK5EWT3R8ItO0BEqynElYhWibRqDg/132';
+// $headimg = imagecreatefromstring(file_get_contents($headimgurl));
+// imagecopymerge($img, $headimg, 50, 1000, 0, 0, 120, 120, 100);
+// imagecopyresampled($img, $headimg, 90, 900, 0, 0, 120, 120, 120, 120);
+
+ // 添加文字
+// imagettftext($img, 18, 0, 220, 1100, 250, public_path().'static/simheittf.ttf', '超级凉面...邀您关注');
+
+ //填充画布(背景色)
+ imagefill($img,0,0, $while);
+
+ // 组合二维码图片 ,坐标:x:246; y:959
+ $prefixPng = 'data:image/png;base64,';
+ $qrStr = base64_decode(str_replace($prefixPng,"",$srcQr));
+ $qrImg = @imagecreatefromstring($qrStr);
+ list($qrWidth, $qrHeight) = getimagesize($srcQr);
+ if(!$qrImg) {
+ imagedestroy($img);
+ throw new \Exception('无效二维码');
+ }
+ $imgQrW = $imgQrH = 274;
+ imagecopyresampled($img, $qrImg, 456, 959, 0, 0, $imgQrW, $imgQrH, $qrWidth, $qrHeight);
+
+ imagedestroy($qrImg);
+ //4、输出与保存最终图像(保存文件或返回base64)
+
+
+ if (empty($savePath)) {
+ /* 返回base64 */
+ ob_start();
+ if ($ext == 'image/jpeg') {
+ imagejpeg($img);
+ } else {
+ imagepng($img);
+ }
+
+ $imgData = ob_get_contents();
+ ob_end_clean();
+ imagedestroy($img);
+
+ $prefix = 'data:image/jpg/png/gif;base64,';
+ return $prefix.base64_encode($imgData);
+
+ } else {
+ /* 保存到文件*/
+ $fileName = md5(microtime(true)).'.'.$extName;
+ $saveFile = $savePath."/".$fileName;
+ if ($ext == 'image/jpeg') {
+ imagejpeg($img, $saveFile);
+ } else {
+ imagepng($img, $saveFile);
+ }
+ }
+
+ /*
+ * 输出显示
+ header("content-type: image/png");
+ imagepng($img);
+ */
+
+ //5、释放画布资源
+ imagedestroy($img);
+ } catch (\Exception $e) {
+ throw $e;
+ }
+
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/app/service/Image.php b/app/service/Image.php
new file mode 100644
index 0000000..d653ce2
--- /dev/null
+++ b/app/service/Image.php
@@ -0,0 +1,130 @@
+getRootPath() . 'public/' . ltrim($src,'/');
+ if(is_file($realPath)){
+ $img = TImage::open($realPath);
+ list($img_w,$img_h) = $img->size();
+ if($max > 0 && $img_w > $max){
+ $img->thumb($max, $max * ($img_h / $img_w))->save($realPath);
+ }
+ }
+ }
+
+ /**
+ * 添加水印
+ * milo
+ * 2018-01-17
+ */
+ public static function mark($src)
+ {
+ $rootPath = app()->getRootPath();
+ if(!empty($src)){
+ $system = System::getSystem();
+ $realPath = $rootPath . 'public/' . ltrim($src, '/');
+ if(is_file($realPath)){
+ if($system['is_mark']){
+ $mark = $rootPath . ltrim($system['mark_img'], '/');
+ if(is_file($mark)){
+ $mark_position = $system['mark_position']??5;
+ $mark_opacity = $system['mark_opacity']??50;
+ $img = TImage::Open($realPath);
+ $img->water($mark,$mark_position,$mark_opacity)->save($realPath);
+ }
+ }
+ }
+ }
+ }
+
+ //获取水印位置键值对
+ public static function getMarkPosition()
+ {
+ return [
+ "1" => '上左',
+ "2" => '上中',
+ "3" => '上右',
+ "4" => '中左',
+ "5" => '正中',
+ "6" => '中右',
+ "7" => '下左',
+ "8" => '下中',
+ "9" => '下右'
+ ];
+ }
+
+ /**
+ * 删除图片
+ * milo
+ * 2018-01-15
+ */
+ public static function delImg($src)
+ {
+ if(!empty(trim($src))){
+ $realPath = app()->getRootPath() . 'public/' . ltrim($src, '/');
+ if (file_exists($realPath)) {
+ $info = pathinfo($realPath);
+ $source = $info['dirname'] . "/" . $info['filename'] . '*.' . $info['extension'];
+ foreach(glob($source) as $filename){
+ if(is_file($filename)){
+ unlink($filename);
+ }
+ }
+ clearstatcache();// 清除缓存
+ }
+
+ }
+ }
+ /**
+ * 获取缩略图
+ * milo
+ * 2019-10-24修改
+ * 避免跨平台出错,目录分隔符全部转换为'/'
+ * app()->getRuntimePath() = app()->getRootPath().'runtime/当前应用模块(api)/'
+ */
+ public static function getThumb($src,$width=0,$height=0,$type = TImage::THUMB_CENTER)
+ {
+ if(empty($src)){
+ return '';
+ }
+ $rootPath = app()->getRootPath();
+ $realPath = $rootPath . 'public/' . ltrim($src, '/');
+ $realPath = str_replace('\\', '/', $realPath);
+ if(!file_exists($realPath)){
+ return '';
+ }
+ $info = pathinfo($src);
+ if($width <= 0 && $height <= 0){ //高宽都小于或等于0,则返回原图片
+ return $src;
+ }
+ $image = TImage::open($realPath);
+ list($imageWidth, $imageHeight) = $image->size();
+ if($width <= 0){
+ $width = floor($height * ($imageWidth / $imageHeight));
+ }elseif($height <= 0){
+ $height = floor($width * ($imageHeight / $imageWidth));
+ }
+ if($width >= $imageWidth || $height >= $imageHeight){
+ return $src;
+ }
+ $thumbName = $info['dirname']. "/" .$info['filename'].'_'.$width.'_'.$height.'.'.$info['extension'];
+ $realThumbName = $rootPath . 'public/' . ltrim($thumbName, '/');
+ $realThumbName = str_replace('\\', '/', $realThumbName);
+ if(!file_exists($realThumbName)){
+ $image->thumb($width, $height, $type)->save($realThumbName);
+ }
+ return str_replace('\\', '/', $thumbName);
+ }
+}
diff --git a/app/service/Jwt.php b/app/service/Jwt.php
new file mode 100644
index 0000000..ef25d15
--- /dev/null
+++ b/app/service/Jwt.php
@@ -0,0 +1,133 @@
+builder()
+ // Configures the issuer (iss claim)
+ ->issuedBy(self::$iss)
+ // Configures the audience (aud claim)
+ ->permittedFor(self::$aud)
+ // Configures the id (jti claim)
+ // ->identifiedBy($this->jti)
+ // Configures the time that the token was issue (iat claim)
+ ->issuedAt($now)
+ // Configures the expiration time of the token (exp claim)
+ ->expiresAt($now->modify(sprintf('+%d seconds', $expire)))
+ // Configures a new claim, called "uid"
+ ->withClaim('data', $data)
+ // Configures a new header, called "foo"
+ // ->withHeader('foo', 'bar')
+ // Builds a new token
+ ->getToken(self::config()->signer(), self::config()->signingKey());
+
+ return $token->toString();
+ }
+
+ /**
+ * 解析
+ *
+ * @param string $tokenStr
+ * @return array|mixed
+ */
+ public static function parse(string $tokenStr)
+ {
+ $config = self::config();
+
+ try {
+ $token = $config->parser()->parse($tokenStr);
+ assert($token instanceof UnencryptedToken);
+ return $token->claims()->all()['data'] ?? [];
+ } catch (Exception $e) {
+ return [];
+ }
+ }
+
+ /**
+ * 验证token
+ *
+ * @param string $tokenStr
+ * @return bool
+ */
+ public static function validate(string $tokenStr): bool
+ {
+ $config = self::config();
+ try {
+ $token = $config->parser()->parse($tokenStr);
+ assert($token instanceof UnencryptedToken);
+
+ //验证签发人iss是否正确
+ $validateIssued = new IssuedBy(self::$iss);
+ $config->setValidationConstraints($validateIssued);
+ //验证客户端aud是否匹配
+ $validateAud = new PermittedFor(self::$aud);
+ $config->setValidationConstraints($validateAud);
+
+ //验证是否过期 exp
+ $timezone = new DateTimeZone('Asia/Shanghai');
+ $now = new SystemClock($timezone);
+ $validateExpired = new LooseValidAt($now);
+ $config->setValidationConstraints($validateExpired);
+
+ $constraints = $config->validationConstraints();
+
+ return $config->validator()->validate($token, ...$constraints);
+ } catch (Exception $e) {
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/service/Kd100.php b/app/service/Kd100.php
new file mode 100644
index 0000000..fe68dd4
--- /dev/null
+++ b/app/service/Kd100.php
@@ -0,0 +1,79 @@
+ $com, //快递公司编码, 一律用小写字母
+ 'num' => $num, //快递单号
+ // 'phone' => '', //手机号
+ // 'from' => '', //出发地城市
+ // 'to' => '', //目的地城市
+ // 'resultv2' => '1' //开启行政区域解析
+ ];
+
+ //请求参数
+ $post_data = [];
+ $post_data["customer"] = self::$customer;
+ $post_data["param"] = json_encode($param);
+ $sign = md5($post_data["param"].self::$key.$post_data["customer"]);
+ $post_data["sign"] = strtoupper($sign);
+
+ $params = "";
+ foreach ($post_data as $k => $v) {
+ $params .= "$k=".urlencode($v)."&"; //默认UTF-8编码格式
+ }
+ $post_data = substr($params, 0, -1);
+
+ //发送post请求
+ $ch = curl_init();
+ curl_setopt($ch, CURLOPT_POST, 1);
+ curl_setopt($ch, CURLOPT_HEADER, 0);
+ curl_setopt($ch, CURLOPT_URL, self::$url);
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+ $result = curl_exec($ch);
+ return json_decode($result, $returnArray);
+ }
+
+ /**
+ * 状态
+ *
+ * @return string[]
+ */
+ public static function state(): array
+ {
+ return [
+ 0 => '在途',
+ 1 => '揽收',
+ 2 => '疑难',
+ 3 => '签收',
+ 4 => '退签',
+ 5 => '派件',
+ 6 => '退回',
+ 7 => '转单',
+ 10 => '待清关',
+ 11 => '清关中',
+ 12 => '已清关',
+ 13 => '清关异常',
+ 14 => '收件人拒签',
+ ];
+ }
+
+ /**
+ * 不在继续查询的配送状态
+ * @return int[]
+ */
+ public static function unSearchState(): array
+ {
+ return [3,4,12];
+ }
+}
diff --git a/app/service/Math.php b/app/service/Math.php
new file mode 100644
index 0000000..37e6a67
--- /dev/null
+++ b/app/service/Math.php
@@ -0,0 +1,60 @@
+model = $model;
+ } elseif (strpos($class, '\\repository') > 0) {
+ $model = str_replace('\\repository', '\\model', $class);
+ //去掉末尾Repository app\model\AccountRepository =》 app\model\Account
+ $model = substr($model, 0, strlen($model) - strlen('Repository'));
+ if (class_exists($model)) {
+ $obj->model = new $model;
+ }
+ }
+
+ self::$objects[$class] = $obj;
+ return $obj;
+ }
+
+ /**
+ * @param callable $callback
+ * @param mixed $failReturn
+ * @param bool $transaction
+ * @return mixed
+ * @throws RepositoryException
+ */
+ protected function access(callable $callback, $failReturn, bool $transaction = false)
+ {
+ $exception = null;
+ try {
+ if ($transaction) {
+ Db::startTrans();
+ }
+
+ $r = $callback();
+
+ if ($transaction) {
+ Db::commit();
+ }
+
+ if ($r) {
+ return $r;
+ }
+
+ if ($failReturn instanceof Exception) {
+ return null;
+ }
+
+ return $failReturn;
+ } catch (Exception $e) {
+ if ($transaction) {
+ Db::rollback();
+ }
+
+ if ($e instanceof RepositoryException) {
+ throw $e;
+ }
+
+ $name = 'Domain - Repository - 未知错误';
+
+ $traces = $e->getTrace();
+
+ foreach ($traces as $i => $trace) {
+ if (!empty($trace['class']) && $trace['class'] === Repository::class && $trace['function'] === 'access') {
+ $trace = $traces[$i - 2] ?? null;
+ break;
+ }
+ }
+
+ if (!empty($trace) && !empty($trace['file'][1]) && !empty($trace['line'])) {
+ $parts = explode('application\\', $trace['file']);
+ if (!empty($parts[1])) {
+ $name = $parts[1].':'.$trace['line'];
+ } else {
+ $name = $trace['file'].':'.$trace['line'];
+ }
+ }
+
+ $exception = $e;
+
+ $line = '['.$name.'] '.$e->getMessage();
+ Log::error($line);
+
+ throw new RepositoryException('Repository异常'.(Env::get('app_debug') ? ': '.$line : ''), 5009);
+ } finally {
+ if ($exception && $failReturn instanceof RepositoryException) {
+ if (Env::get('app_debug')) {
+ $failReturn = new RepositoryException($failReturn->getMessage().': '.$exception->getMessage(),
+ $failReturn->getCode());
+ }
+ throw $failReturn;
+ }
+ }
+ }
+
+ /**
+ * 获取当前model对象
+ *
+ * @return Model
+ */
+ public function getModel(): Model
+ {
+ return $this->model;
+ }
+
+ /**
+ * 根据条件查询列表
+ *
+ * @param array $where 查询条件
+ * @param array $fields 查询字段 []表示全部
+ * @param int $page 默认第一页 0不限制
+ * @param int $limit 限制条数 0不限制
+ * @param callable|null $callback 更为复杂的条件 使用闭包查询
+ * @return array
+ * @throws RepositoryException
+ */
+ public function findList(array $where = [], array $fields = [], int $page = 1, int $limit = 0, callable $callback = null, array $order = []): ?array
+ {
+ $failData = [
+ 'total' => 0,
+ 'current' => $page,
+ 'size' => $limit,
+ 'list' => new Collection(),
+ ];
+ return $this->access(function () use ($where, $fields, $page, $limit, $callback, $order) {
+ return $this->model->findList($where, $fields, $page, $limit, $callback, $order);
+ }, $failData);
+ }
+
+ /**
+ * 根据条件查询列表[带分页 适用于后台]
+ *
+ * @param array $data 查询数据
+ * @param array $pageParams 分页参数
+ * @param callable|null $callback 复杂查询条件 使用闭包查询
+ * @return Paginator
+ */
+ public function findListWithPaginate(array $data = [], array $pageParams = [], callable $callback = null): Paginator
+ {
+ return $this->model->findListWithPaginate($data, $pageParams, $callback);
+ }
+
+ /**
+ * 根据主键 ID 查询
+ *
+ * @param int $id ID
+ * @param array $fields 要返回的字段,默认全部
+ * @return Mixed
+ * @throws RepositoryException
+ */
+ public function findById(int $id, array $fields = [], callable $callback = null)
+ {
+ return $this->access(function () use ($id, $fields, $callback) {
+ return $this->model->findById($id, $fields, $callback);
+ }, null);
+ }
+
+ /**
+ * @param array $where
+ * @param array $fields
+ * @return array|Model|null
+ * @throws DataNotFoundException
+ * @throws DbException
+ * @throws ModelNotFoundException
+ */
+ public function findOneByWhere(array $where, array $fields = [])
+ {
+ return $this->model->field($fields)->where($where)->find();
+ }
+
+ /**
+ * 创建
+ *
+ * @param array $data 数据
+ * @return Model
+ * @throws RepositoryException
+ */
+ public function create(array $data): Model
+ {
+ return $this->access(function () use ($data) {
+ return $this->model->create($data);
+ }, new RepositoryException('创建失败'));
+ }
+
+ /**
+ * 更新
+ *
+ * @param array $data 数据
+ * @param array $where 条件
+ * @return bool|Exception
+ * @throws RepositoryException
+ */
+ public function update(array $data, array $where): bool
+ {
+ return $this->access(function () use ($data, $where) {
+ return $this->model->where($where)->find()->save($data);
+ }, new RepositoryException('更新失败'));
+ }
+
+ /**
+ * 删除
+ *
+ * @param array $where 删除条件
+ * @param bool $softDelete 是否软删除 默认false
+ * @param string $softDeleteTime 删除时间 softDelete=true时有效
+ * @param string $softDeleteField 软删除字段 softDelete=true时有效
+ * @return bool
+ * @throws RepositoryException
+ */
+ public function delete(array $where, bool $softDelete = false, string $softDeleteTime = '', string $softDeleteField = 'deleted_at'): bool
+ {
+ return $this->access(function () use ($where, $softDelete, $softDeleteField, $softDeleteTime) {
+ // 注意:如果model中引入了软删除trait,$softDelete又设置false 将无法正确删除
+ return $this->model->where($where)
+ ->when($softDelete, function ($q) use ($softDeleteField, $softDeleteTime) {
+ $softDeleteTime = $softDeleteTime ?: date('Y-m-d H:i:s');
+ $q->useSoftDelete($softDeleteField, $softDeleteTime);
+ })->delete();
+ }, false);
+ }
+
+ /**
+ * 排序
+ *
+ * @param int $id 排序ID
+ * @param string $type 排序类型 向上、向下
+ * @param int $num 移动位数
+ * @param string $listType 列表的排序类型 降序|升序
+ * @param array $where 额外条件 格式如:
+ * $map[] = ['name','like','think'];
+ * $map[] = ['status','=',1];
+ * @return array
+ */
+ public function sort(int $id,string $type,int $num,string $listType, array $where = []): array
+ {
+ return $this->model->sort($id, $type, $num, $listType, $where);
+ }
+
+ /**
+ * 日志记录
+ *
+ * @param string $msg
+ * @param Exception|null $e
+ * @param string $level
+ * @param string $channel
+ */
+ public static function log(string $msg, Exception $e = null, string $level = 'error', string $channel = 'file')
+ {
+ if ($e != null) {
+ $msg = sprintf("[%s]%s:%s %s", $msg, $e->getFile(), $e->getLine(), $e->getMessage());
+ } else {
+ $msg = sprintf("%s", $msg);
+
+ }
+ Log::channel($channel)->$level($msg);
+ }
+}
diff --git a/app/service/Sms.php b/app/service/Sms.php
new file mode 100644
index 0000000..27f8e65
--- /dev/null
+++ b/app/service/Sms.php
@@ -0,0 +1,189 @@
+ $code
+ // "product" => "大头拍卖"
+ );
+
+ // fixme 可选: 设置发送短信流水号
+ // $params['OutId'] = "12345";
+
+ // fixme 可选: 上行短信扩展码, 扩展码字段控制在7位或以下,无特殊需求用户请忽略此字段
+ // $params['SmsUpExtendCode'] = "1234567";
+
+
+ // *** 需用户填写部分结束, 以下代码若无必要无需更改 ***
+ if (!empty($params["TemplateParam"]) && is_array($params["TemplateParam"])) {
+ $params["TemplateParam"] = json_encode($params["TemplateParam"], JSON_UNESCAPED_UNICODE);
+ }
+
+ // 初始化SignatureHelper实例用于设置参数,签名以及发送请求
+ $helper = new SignatureHelper();
+
+ // 此处可能会抛出异常,注意catch
+ $res = $helper->request(
+ $accessKeyId,
+ $accessKeySecret,
+ "apisms.landui.com",
+ array_merge($params, array(
+ "RegionId" => "cn-hangzhou",
+ "Action" => "SendSms",
+ "Version" => "2017-05-25",
+ )),
+ $security
+ );
+
+ if (isset($res->Code) && $res->Code == 'OK') {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * 发送短信
+ */
+ public static function sendContent(string $tel, array $content, string $templateCode = '130'): bool
+ {
+ $params = array();
+
+ // *** 需用户填写部分 ***
+ // fixme 必填:是否启用https
+ $security = false;
+
+ // fixme 必填: 请参阅 https://ak-console.aliyun.com/ 取得您的AK信息
+ $accessKeyId = self::$accessKeyId;
+ $accessKeySecret = self::$accessKeySecret;
+
+ // fixme 必填: 短信接收号码
+ $params["PhoneNumbers"] = $tel;
+
+ // fixme 必填: 短信签名,应严格按"签名名称"填写,请参考: https://dysms.console.aliyun.com/dysms.htm#/develop/sign
+ $params["SignName"] = "大头拍卖";
+
+ // fixme 必填: 短信模板Code,应严格按"模板CODE"填写, 请参考: https://dysms.console.aliyun.com/dysms.htm#/develop/template
+ $params["TemplateCode"] = $templateCode;//"code";
+
+ $params['TemplateParam'] = self::handleContent($templateCode, $content);
+
+ // *** 需用户填写部分结束, 以下代码若无必要无需更改 ***
+ if (!empty($params["TemplateParam"]) && is_array($params["TemplateParam"])) {
+ $params["TemplateParam"] = json_encode($params["TemplateParam"], JSON_UNESCAPED_UNICODE);
+ }
+
+ // 初始化SignatureHelper实例用于设置参数,签名以及发送请求
+ $helper = new SignatureHelper();
+
+ // 此处可能会抛出异常,注意catch
+ $res = $helper->request(
+ $accessKeyId,
+ $accessKeySecret,
+ "apisms.landui.com",
+ array_merge($params, array(
+ "RegionId" => "cn-hangzhou",
+ "Action" => "SendSms",
+ "Version" => "2017-05-25",
+ )),
+ $security
+ );
+
+ if (isset($res->Code) && $res->Code == 'OK') {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * 处理内容
+ *
+ * @param string $templateCode
+ * @param array $content
+ * @return array|string[]
+ * @throws Exception
+ */
+ private static function handleContent(string $templateCode, array $content): array
+ {
+ switch ($templateCode) {
+ //788 活动开始通知
+ case self::TEMPLATE_BEGIN:
+ if (!isset($content['name']) || !isset($content['time'])) {
+ throw new Exception('参数错误');
+ }
+ $res = [
+ "name" => $content['name'] ?? '',
+ "time" => $content['time'] ?? ''
+ ];
+ break;
+ //789 竞拍成功
+ case self::TEMPLATE_SUCCESS:
+ if (!isset($content['name'])) {
+ throw new Exception('参数错误');
+ }
+ $res = [
+ "name" => $content['name'] ?? '',
+ ];
+ break;
+ //790 发货通知
+ //791 新订单通知管理员
+ case self::TEMPLATE_SHIPPED:
+ case self::TEMPLATE_NEW_ORDER:
+ if (!isset($content['order'])) {
+ throw new Exception('参数错误');
+ }
+ $res = [
+ "order" => $content['order'] ?? '',
+ ];
+ break;
+ default:
+ //默认130 短信验证码
+ if (!isset($content['code'])) {
+ throw new Exception('参数错误');
+ }
+ $res = [
+ "code" => $content['code'] ?? ''
+ ];
+ }
+ return $res;
+ }
+}
diff --git a/app/service/Test.php b/app/service/Test.php
new file mode 100644
index 0000000..ea46ace
--- /dev/null
+++ b/app/service/Test.php
@@ -0,0 +1,27 @@
+getRootPath() . ltrim($path, '/');
+ if (file_exists($realPath)) {
+ $info = pathinfo($realPath);
+ $source = $info['dirname'] . "/" . $info['filename'] . '*.' . $info['extension'];
+ foreach(glob($source) as $filename){
+ if(is_file($filename)){
+ unlink($filename);
+ }
+ }
+ clearstatcache();// 清除缓存
+ }
+ }
+ }
+
+ /**
+ * 删除目录下的所有文件和子目录
+ * 调用完毕后请用clearstatcache()清理缓存
+ */
+ public static function removeByPath($path)
+ {
+ if(is_dir($path)) {
+ if($handle = @opendir($path)) {
+ while (($file = readdir($handle)) !== false){
+ if($file != '.' && $file != '..') {
+ $child = $path.'/'.$file;
+ is_dir($child) ? self::removeByPath($child) : @unlink($child);
+ }
+ }
+ }
+ closedir($handle);
+ } elseif (is_file($path)) {
+ @unlink($path);
+ } else {
+ return false;
+ }
+ return true;
+ }
+
+ //去除字符串空格
+ public static function trimSpace($str)
+ {
+ return str_replace(' ', '', trim($str));
+ }
+}
diff --git a/app/service/sms/SignatureHelper.php b/app/service/sms/SignatureHelper.php
new file mode 100644
index 0000000..3b07dde
--- /dev/null
+++ b/app/service/sms/SignatureHelper.php
@@ -0,0 +1,97 @@
+ "HMAC-SHA1",
+ "SignatureNonce" => uniqid(mt_rand(0,0xffff), true),
+ "SignatureVersion" => "1.0",
+ "AccessKeyId" => $accessKeyId,
+ "Timestamp" => gmdate("Y-m-d\TH:i:s\Z"),
+ "Format" => "JSON",
+ ), $params);
+ ksort($apiParams);
+
+ $sortedQueryStringTmp = "";
+ foreach ($apiParams as $key => $value) {
+ $sortedQueryStringTmp .= "&" . $this->encode($key) . "=" . $this->encode($value);
+ }
+
+ $stringToSign = "${method}&%2F&" . $this->encode(substr($sortedQueryStringTmp, 1));
+
+ $sign = base64_encode(hash_hmac("sha1", $stringToSign, $accessKeySecret . "&",true));
+
+ $signature = $this->encode($sign);
+
+ $url = ($security ? 'https' : 'http')."://{$domain}/";
+
+ try {
+ $content = $this->fetchContent($url, $method, "Signature={$signature}{$sortedQueryStringTmp}");
+ return json_decode($content);
+ } catch( \Exception $e) {
+ return false;
+ }
+ }
+
+ private function encode($str)
+ {
+ $res = urlencode($str);
+ $res = preg_replace("/\+/", "%20", $res);
+ $res = preg_replace("/\*/", "%2A", $res);
+ $res = preg_replace("/%7E/", "~", $res);
+ return $res;
+ }
+
+ private function fetchContent($url, $method, $body) {
+ $ch = curl_init();
+
+ if($method == 'POST') {
+ curl_setopt($ch, CURLOPT_POST, 1);//post提交方式
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
+ } else {
+ $url .= '?'.$body;
+ }
+
+ curl_setopt($ch, CURLOPT_URL, $url);
+ curl_setopt($ch, CURLOPT_TIMEOUT, 5);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+ curl_setopt($ch, CURLOPT_HTTPHEADER, array(
+ "x-sdk-client" => "php/2.0.0"
+ ));
+
+ if(substr($url, 0,5) == 'https') {
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
+ }
+
+ $rtn = curl_exec($ch);
+
+ if($rtn === false) {
+ // 大多由设置等原因引起,一般无法保障后续逻辑正常执行,
+ // 所以这里触发的是E_USER_ERROR,会终止脚本执行,无法被try...catch捕获,需要用户排查环境、网络等故障
+ trigger_error("[CURL_" . curl_errno($ch) . "]: " . curl_error($ch), E_USER_ERROR);
+ }
+ curl_close($ch);
+
+ return $rtn;
+ }
+}
\ No newline at end of file
diff --git a/app/service/wx/Wechat.php b/app/service/wx/Wechat.php
new file mode 100644
index 0000000..6c404f9
--- /dev/null
+++ b/app/service/wx/Wechat.php
@@ -0,0 +1,46 @@
+ $conf['appId'],
+ 'secret' => $conf['appSecret'],
+
+ // 指定 API 调用返回结果的类型:array(default)/collection/object/raw/自定义类名
+ 'response_type' => 'array',
+
+ //...
+ ];
+
+ self::$app = Factory::officialAccount($config);
+ }
+ return self::$app;
+ }
+}
\ No newline at end of file
diff --git a/app/service/wx/WechatApplets.php b/app/service/wx/WechatApplets.php
new file mode 100644
index 0000000..ceb9280
--- /dev/null
+++ b/app/service/wx/WechatApplets.php
@@ -0,0 +1,112 @@
+ $conf['applets_appId'],
+ 'secret' => $conf['applets_appSecret'],
+ // 返回数据类型 array | xml
+ 'response_type' => 'array',
+ ];
+ self::$app = Factory::miniProgram($config);
+ }
+ return self::$app;
+ }
+
+ /**
+ * 生成微信小程序链接
+ *
+ * @param string $path
+ * @param string $sourceCode
+ * @param bool $tokenRefresh 是否强制刷新access_token 默认false 非特殊条件请勿设为true
+ * @return string
+ * @throws GuzzleException
+ * @throws HttpException
+ * @throws InvalidArgumentException
+ * @throws InvalidConfigException
+ * @throws RuntimeException
+ * @throws \Psr\SimpleCache\InvalidArgumentException
+ */
+ public static function generateActivityUrl(string $path, string $sourceCode, bool $tokenRefresh = false): string
+ {
+ $accessToken = self::getInstance()->access_token;
+
+ $client = new Client();
+
+ $url = 'https://api.weixin.qq.com/wxa/generate_urllink?access_token=';
+
+ $params = [];
+ $query = '';
+ $params['path'] = 'pages/tabbar/pagehome/pagehome';//首页路径
+ if (!empty($path)) {
+ $pathArr = explode('?', $path);
+ $query = isset($pathArr[1]) ? $pathArr[1].'&' : '';
+ $params['path'] = $pathArr[0];
+ }
+
+ $params['query'] = $query.'channel=activity&source_code='.$sourceCode;
+ $jsonStr = !empty($params) ? json_encode($params, JSON_UNESCAPED_UNICODE) : '';
+
+ Log::info('【小程序链接生成请求参数】'.$jsonStr);
+
+ $response = $client->request('POST', $url.($accessToken->getToken($tokenRefresh)['access_token'] ?? ''), [
+ 'body' => $jsonStr
+ ]);
+
+ $res = json_decode($response->getBody(), true);
+ Log::info('【小程序链接生成响应】'.json_encode($res, JSON_UNESCAPED_UNICODE));
+ if ($res['errcode'] == 0) {
+ return $res['url_link'] ?? '';
+ }
+
+ if ($res['errcode'] == 40001 && $tokenRefresh == false) {
+ // 可能是token过期 刷新一次
+ // tokenRefresh 防止无限循环
+ return self::generateActivityUrl($path, $sourceCode, true);
+ }
+
+ throw new Exception('链接生成失败 '.$res['errmsg'] ?? '');
+ }
+}
\ No newline at end of file
diff --git a/app/service/wx/WechatPay.php b/app/service/wx/WechatPay.php
new file mode 100644
index 0000000..dd2b117
--- /dev/null
+++ b/app/service/wx/WechatPay.php
@@ -0,0 +1,47 @@
+ $conf['applets_appId'],//此处使用的小程序APPID
+ 'mch_id' => $conf['mchid'],
+ 'key' => $conf['key'], // API 密钥
+
+ // 如需使用敏感接口(如退款、发送红包等)需要配置 API 证书路径(登录商户平台下载 API 证书)
+ 'cert_path' => root_path().'cert/apiclient_cert.pem', // XXX: 绝对路径!!!!
+ 'key_path' => root_path().'cert/apiclient_key.pem', // XXX: 绝对路径!!!!
+
+ 'notify_url' => $conf['applets_notify_url'], // 你也可以在下单时单独设置来想覆盖它
+ ];
+ self::$app = Factory::payment($config);
+ }
+ return self::$app;
+ }
+}
\ No newline at end of file
diff --git a/app/traits/SignInTrait.php b/app/traits/SignInTrait.php
new file mode 100644
index 0000000..68a1916
--- /dev/null
+++ b/app/traits/SignInTrait.php
@@ -0,0 +1,159 @@
+whereBetweenTime("created_at", date("Y-m-d 00:00:00", time()), date("Y-m-d 23:59:59", time()))
+ ->count();
+ return ($signInCount > 0) ? true : false;
+ }
+
+ /**
+ * 线上签到
+ *
+ * @param object $account
+ */
+ public function SignInOnline($account, $score)
+ {
+ //删除重复的记录
+ AccountSignOnline::
+ where('account_id', $account["id"])
+ ->whereBetweenTime("created_at", date("Y-m-d 00:00:00", time()), date("Y-m-d 23:59:59", time()))
+ ->delete();
+
+ //奖励积分
+ $this->accountReward($account, Task::reward_type_score, $score);
+
+ //更新最后签到时间和连续签到次数
+ $account->save(["continuity_sign" => ($account['continuity_sign'] + 1), "last_sign_online" => date("Y-m-d H:i:s")]);
+
+ //写入签到记录
+ AccountSignOnline::create(["account_id" => $account["id"], "created_at" => date("Y-m-d H:i:s"), "score" => $score]);
+ }
+
+
+ /**
+ * 检查线下签到
+ *
+ * @param int $accountId
+ */
+ public function checkSignIn($accountId)
+ {
+ $signInCount = AccountSign::
+ where('account_id', $accountId)
+ ->whereBetweenTime("created_at", date("Y-m-d 00:00:00", time()), date("Y-m-d 23:59:59", time()))
+ ->count();
+ return ($signInCount > 0) ? true : false;
+ }
+
+ /**
+ * 线下签到
+ *
+ * @param int $accountId
+ */
+ public function SignIn($accountId)
+ {
+ AccountSign::where('account_id', $accountId)
+ ->whereBetweenTime("created_at", date("Y-m-d 00:00:00", time()), date("Y-m-d 23:59:59", time()))
+ ->delete();
+ AccountSign::create(["account_id" => $accountId, "created_at" => date("Y-m-d H:i:s")]);
+ }
+
+
+ /**
+ * 操作用户孔雀币
+ * @param $account array|object 用户对象
+ * @param $rewardType string 奖励类型
+ * @param $totalReward int 奖励总数
+ * */
+ public function accountReward($account, $rewardType, $totalReward)
+ {
+ //写入--积分孔雀币日志
+ AccountDataLog::log($account['id'],
+ "签到奖励积分",
+ $totalReward,
+ $rewardType,
+ AccountDataLog::ACTION_SIGN,
+ $account[$rewardType] + $totalReward
+ );
+
+ //加积分
+ $accountSaveData = [$rewardType => ($account[$rewardType] + $totalReward)];
+ $account->save($accountSaveData);
+
+ }
+
+ /**
+ * 本周的签到记录
+ *
+ * @param string $accountId
+ */
+ public function weedSignInOnlineRecord($accountId, $startDate, $endData)
+ {
+ return AccountSignOnline::where('account_id', $accountId)
+ ->whereBetweenTime("created_at", $startDate, $endData)
+ ->order("created_at desc")
+ ->select();
+ }
+
+ /**
+ * 签到记录分页
+ *
+ * @param string $accountId
+ */
+ public function onlineSignRecordList($accountId, $page, $size)
+ {
+ return AccountSignOnline::where('account_id', $accountId)
+ ->page($page, $size)
+ ->field("created_at,score")
+ ->order("created_at desc")
+ ->select();
+ }
+
+ /**
+ * 验证连续签到次数
+ * 上次签到 距离当天已经超过1天 连续签到中断
+ * @param object $account
+ */
+ public function checkContinuitySign($account)
+ {
+ if (empty($account["last_sign_online"])) {
+ $account->save(["continuity_sign" => 0]);
+ }
+ $prevDayTime = strtotime(date("Y-m-d", strtotime("-1 day")));//昨天 00:00:00的时间戳
+ $lastSignOnlineTime = strtotime($account["last_sign_online"]);
+
+ //如果上次签到 在一天之前 归零连续签到次数
+ if ($lastSignOnlineTime < $prevDayTime) {
+ $account->save(["continuity_sign" => 0]);
+ }
+ }
+
+ /*
+ * 后台统计当天签到数
+ * */
+ public function todaySignInCount()
+ {
+ return AccountSignOnline::whereBetweenTime("created_at", date("Y-m-d 00:00:00"), date("Y-m-d 23:59:59"))->count();
+ }
+
+
+}
\ No newline at end of file
diff --git a/app/traits/cms/ArticleTrait.php b/app/traits/cms/ArticleTrait.php
new file mode 100644
index 0000000..4c80119
--- /dev/null
+++ b/app/traits/cms/ArticleTrait.php
@@ -0,0 +1,59 @@
+where('status', Menu::STATUS_NORMAL)
+ ->when($type != 'all', function ($q) use ($show) {
+ $q->where('is_show', $show);
+ })
+ ->when($type != 'all', function ($q) use ($type) {
+ $q->where('type', $type);
+ })
+ ->order('sort', 'desc')
+ ->order('id', 'asc')
+ ->select();
+ }
+
+ //递归获取子菜单
+ public function buildMenuChild(int $pid, array $menus, string $childName = 'children'): array
+ {
+ $treeList = [];
+
+ foreach ($menus as $v) {
+ if ($pid == $v['pid']) {
+ $node = $v;
+
+ $node['has_children'] = false;
+ $child = $this->buildMenuChild($v['id'], $menus, $childName);
+ if (!empty($child)) {
+ $node[$childName] = $child;
+ $node['has_children'] = true;
+ }
+
+ $treeList[] = $node;
+
+ }
+ }
+ return $treeList;
+ }
+
+ //菜单权限过滤
+ public function handMenuRule(int $accountId, array $menus): array
+ {
+ $rules = CmsRepository::getInstance()->getUserRules($accountId);
+
+ foreach ($menus as $k => $m) {
+ $menus[$k]['icon'] = !empty($m['icon']) ? 'fa '.$m['icon'] : '';
+ $menus[$k]['href'] = ltrim($m['href'], '/');
+
+ if ($m['pid'] !== 0) {
+ $name = $m['name'];
+ $nameArr = explode(':', $name);
+ if (count($nameArr) <= 1) {
+ $name = $name.':index';
+ }
+
+ if (!in_array($name, $rules)) {
+ unset($menus[$k]);
+ }
+ }
+ }
+ return $menus;
+ }
+
+ /**
+ * 检测批量数据下 是否有子栏目
+ *
+ * @param array $ids
+ * @return bool
+ */
+ public function hasChildrenMenuByIds(array $ids): bool
+ {
+ return Menu::whereIn('pid', $ids)->count() > 0;
+ }
+
+ /**
+ * 批量删除
+ *
+ * @param array $ids
+ * @return bool
+ */
+ public function delMenuByIds(array $ids): bool
+ {
+ return Menu::deleteByIds($ids);
+ }
+}
\ No newline at end of file
diff --git a/app/validate/Account.php b/app/validate/Account.php
new file mode 100644
index 0000000..d16b97f
--- /dev/null
+++ b/app/validate/Account.php
@@ -0,0 +1,32 @@
+ 'require|mobile',
+ 'old_password|旧密码' => 'require|length:6,16',
+ 'password|密码' => 'require|length:6,16',
+ 'confirm_password|确认密码' => 'require|confirm:password|length:6,16',
+ 'password_confirm|确认密码' => 'require|confirm:password|length:6,16',
+ 'code|验证码' => 'require|number|length:6',
+ 'type|类型' => 'require',
+ 'username|用户名' => 'require|min:4',
+
+ 'id_card|身份证' => 'require',
+ 'real_name|真实姓名' => 'require',
+ 'bank_number|银行卡号' => 'require',
+ 'bank_name|银行名称' => 'require',
+ ];
+
+ protected $scene = [
+ 'edit_password' => ['old_password', 'password', 'confirm_password'], //修改密码
+ 'register' => ['username', 'nickname', 'phone', 'password', 'password_confirm', 'code'], //注册
+ 'send_sms' => ['phone', 'type'], //发送短信
+ 'binding' => ['phone', 'code'], //绑定手机号
+ 'real_name' => ['id_card', 'real_name', 'bank_number', 'bank_name'], //实名认证
+ ];
+}
\ No newline at end of file
diff --git a/app/validate/AuthGroup.php b/app/validate/AuthGroup.php
new file mode 100644
index 0000000..cebd727
--- /dev/null
+++ b/app/validate/AuthGroup.php
@@ -0,0 +1,17 @@
+ 'require',
+ 'status' => 'require|number',
+ ];
+ protected $message = [
+ 'title.require' => '角色名称不能为空',
+ 'status.require' => '角色状态必须设置',
+ 'status.number' => '角色状态参数值只能为数字类型',
+ ];
+}
\ No newline at end of file
diff --git a/app/validate/AuthRule.php b/app/validate/AuthRule.php
new file mode 100644
index 0000000..3dc11b7
--- /dev/null
+++ b/app/validate/AuthRule.php
@@ -0,0 +1,19 @@
+ 'require',
+ 'name' => 'require',
+ 'status' =>'require|number',
+ ];
+ protected $message = [
+ 'title.require' => '名称必须',
+ 'name.require'=> '标识必须',
+ 'status.require'=> '显示状态必须传值',
+ 'status.number'=> '显示状态传值只能为数字表示',
+ ];
+}
\ No newline at end of file
diff --git a/app/validate/Block.php b/app/validate/Block.php
new file mode 100644
index 0000000..171a21d
--- /dev/null
+++ b/app/validate/Block.php
@@ -0,0 +1,51 @@
+,开发者QQ群:50304283
+// +----------------------------------------------------------------------
+namespace app\validate;
+
+use think\Validate;
+
+/**
+ * 碎片验证器
+ * @package app\controller\cms\validate
+ */
+class Block extends Validate
+{
+ //定义验证规则
+ protected $rule = [
+ 'name|别名' => [
+ 'require',
+ 'length' => '1,30',
+ 'regex' => '/^[A-Za-z0-9\_]+$/',
+ ],
+ 'title|名称' => [
+ 'require',
+ 'length' => '1,20',
+ ],
+ 'content1|碎片内容' => [
+ 'requireIf' => 'type,1'
+ ],
+ 'content2|碎片内容' => [
+ 'requireIf' => 'type,2'
+ ],
+ 'content3|碎片内容' => [
+ 'requireIf' => 'type,3'
+ ],
+ 'content4|碎片内容' => [
+ 'requireIf' => 'type,4'
+ ],
+ 'content5|碎片内容' => [
+ 'requireIf' => 'type,5'
+ ],
+ 'content6|碎片内容' => [
+ 'requireIf' => 'type,6'
+ ],
+ ];
+}
diff --git a/app/validate/CommonValidate.php b/app/validate/CommonValidate.php
new file mode 100644
index 0000000..c7fb8da
--- /dev/null
+++ b/app/validate/CommonValidate.php
@@ -0,0 +1,17 @@
+ 'require|mobile',
+ 'type|类型' => 'require',
+ ];
+
+ protected $scene = [
+ 'send_sms' => ['phone', 'type'], //发送短信
+ ];
+}
\ No newline at end of file
diff --git a/app/validate/Link.php b/app/validate/Link.php
new file mode 100644
index 0000000..ba7adfc
--- /dev/null
+++ b/app/validate/Link.php
@@ -0,0 +1,16 @@
+ 'require',
+ 'url' => 'url',
+ ];
+ protected $message = [
+ 'title.require' => '名称必须',
+ 'url.url' => '请填写有效的网址,以http://或https://开头'
+ ];
+}
\ No newline at end of file
diff --git a/app/validate/MenuValidate.php b/app/validate/MenuValidate.php
new file mode 100644
index 0000000..08c22c8
--- /dev/null
+++ b/app/validate/MenuValidate.php
@@ -0,0 +1,21 @@
+ 'require|number',
+ 'title|标题' => 'require',
+ 'id|ID' => 'require|number',
+ 'field|字段名' => 'require',
+ 'value|值' => 'require',
+ ];
+
+ protected $scene = [
+ 'menu_add_and_edit' => ['pid', 'title'],//菜单添加
+ 'menu_modify' => ['id', 'field', 'value'],//单元格编辑
+ ];
+}
\ No newline at end of file
diff --git a/app/validate/Slide.php b/app/validate/Slide.php
new file mode 100644
index 0000000..c391c4a
--- /dev/null
+++ b/app/validate/Slide.php
@@ -0,0 +1,17 @@
+ 'require',
+ 'position|位置' => 'require',
+ 'src|轮播图' => 'require',
+ ];
+
+ protected $scene = [
+ 'slide' => ['title', 'position', 'src'],//轮播图
+ ];
+}
\ No newline at end of file
diff --git a/app/validate/Upload.php b/app/validate/Upload.php
new file mode 100644
index 0000000..f34e16a
--- /dev/null
+++ b/app/validate/Upload.php
@@ -0,0 +1,60 @@
+system = System::getSystem();
+ $this->lang = new Lang;
+ }
+
+ //验证图片上传
+ public function checkImage($image)
+ {
+ $ext = str_replace(',', ',', $this->system['img_type']);
+ $size = $this->system['img_size'] * 1024 * 1024;
+ $this->rule = [
+ 'image' => [
+ 'fileExt' => $ext,
+ 'fileSize' => (int)$size
+ ]
+ ];
+ return $this->check(['image' => $image]);
+ }
+
+ //验证视频上传
+ public function checkVideo($video)
+ {
+ $ext = str_replace(',', ',', $this->system['video_type']);
+ $size = $this->system['video_size'] * 1024 * 1024;
+ $this->rule = [
+ 'video' => [
+ 'fileExt' => $ext,
+ 'fileSize' => (int)$size
+ ]
+ ];
+ return $this->check(['video' => $video]);
+ }
+
+ //验证文件上传
+ public function checkFile($file)
+ {
+ $ext = str_replace(',', ',', $this->system['file_type']);
+ $size = $this->system['file_size'] * 1024 * 1024;
+ $this->rule = [
+ 'file' => [
+ 'fileExt' => $ext,
+ 'fileSize' => (int)$size
+ ]
+ ];
+ return $this->check(['file' => $file]);
+ }
+}
\ No newline at end of file
diff --git a/app/widget/Crumbs.php b/app/widget/Crumbs.php
new file mode 100644
index 0000000..c43fa6a
--- /dev/null
+++ b/app/widget/Crumbs.php
@@ -0,0 +1,17 @@
+ Category::getCatesCrumbs($categoryId)
+ ];
+ return View::assign($data)->fetch('public/crumbs');
+ }
+
+}
\ No newline at end of file
diff --git a/app/widget/Menu.php b/app/widget/Menu.php
new file mode 100644
index 0000000..204c052
--- /dev/null
+++ b/app/widget/Menu.php
@@ -0,0 +1,49 @@
+ 0) {
+ $currentFirstId = Category::firstGradeById($categoryId);
+ }
+
+ //产品菜单
+
+ $product_children=Category::alias('c')
+ ->leftJoin('model m', 'c.model_id=m.id')
+ ->field('c.*, m.manager, m.template, m.name as modelName')
+ ->where('c.parent_id', 5)
+ ->order('c.sort','asc')
+ ->select()
+ ->toArray();
+
+ $data = [
+ 'categoryId' => $categoryId,
+ 'menus' => $menus,
+ 'product_children' => $product_children?$product_children:[],
+ ];
+
+
+ $this_menu= \app\model\Category::alias('c')
+ ->leftJoin('model m', 'c.model_id=m.id')
+ ->field('c.*, m.manager, m.name as modelName')
+ ->when(3, function($query) {
+ $query->whereIn('c.id', []);
+ })
+ ->order('sort','asc')
+ ->select()
+ ->toArray();
+ return View::assign($data)->fetch('public/menu');
+ }
+}
\ No newline at end of file
diff --git a/app/widget/Slide.php b/app/widget/Slide.php
new file mode 100644
index 0000000..4677bd7
--- /dev/null
+++ b/app/widget/Slide.php
@@ -0,0 +1,16 @@
+ WSlide::getList(),
+ ];
+ return View::assign($data)->fetch('public/slide');
+ }
+}
\ No newline at end of file
diff --git a/app/widget/manager/Crumbs.php b/app/widget/manager/Crumbs.php
new file mode 100644
index 0000000..9b5e066
--- /dev/null
+++ b/app/widget/manager/Crumbs.php
@@ -0,0 +1,44 @@
+controller());
+ $action = unCamelize($request->action());
+ $controller = str_replace('manager.', '', $controller);
+ $name = $controller.'/'.$action;
+ if ($action == 'index') {
+ $rule = AuthRule::getByTwoName($name, $controller);
+ } else {
+ $rule = AuthRule::getByName($name);
+ }
+ $parent = [];
+ if (!empty($rule) && $rule['parent_id']) {
+ $parent = AuthRule::getById($rule['parent_id']);
+ }
+ $cateCrumbs = [];
+ $isContent = false;
+ if ($controller == 'content') {
+ $isContent = true;
+ $categoryId = $request->param('category_id', 0);
+ if (is_numeric($categoryId) && $categoryId > 0) {
+ $cateCrumbs = Category::getCatesCrumbs($categoryId);
+ }
+ }
+
+ $data = [
+ 'rule' => $rule,
+ 'parent' => $parent,
+ 'isContent' => $isContent,
+ 'cateCrumbs' => $cateCrumbs
+ ];
+ return View::assign($data)->fetch('manager/widget/crumbs');
+ }
+}
\ No newline at end of file
diff --git a/app/widget/manager/Menu.php b/app/widget/manager/Menu.php
new file mode 100644
index 0000000..5ae38bb
--- /dev/null
+++ b/app/widget/manager/Menu.php
@@ -0,0 +1,95 @@
+getMenuRules($rules);
+
+ $current = strtolower(request()->controller());
+ $current = str_replace('manager.', '', $current);
+ $currentAction = strtolower($current.'/'.request()->action());
+ // message 留言管理是否集成在内容管理中,后期开发中根据情况调整
+ if(in_array($current, ['article', 'product', 'page'], true)){
+ $current = 'content';
+ }
+ if($auth['groupId'] == 1) {
+ $menus = $this->getMenus(Category::getList(false));
+ } else {
+ $menus = $this->getMenus(Category::getList(true, $authCates));
+ }
+ foreach ($menus as $key => $menu) {
+ if ($menu['backend_show'] == 0) {
+ unset($menus[$key]);
+ }
+ }
+ $data = [
+ 'rules' => $menuRules,
+ 'categoryId' => $categoryId,
+ 'menus' => $menus,
+ 'current' => $current,
+ 'currentAction' => $currentAction
+ ];
+ return View::assign($data)->fetch('manager/widget/left');
+ }
+
+ /**
+ * 过滤出权限菜单
+ * @param $rules
+ * @return array
+ */
+ private function getMenuRules($rules)
+ {
+ $menuRules = [];
+ if (!empty($rules)) {
+ foreach ($rules as $rule) {
+ $hasChildren = $rule['hasChildren'] ?? false;
+ if ($hasChildren) {
+ $rule['children'] = $this->getMenuRules($rule['children']);
+ if(count($rule['children']) > 0) {
+ $rule['status'] = 1;
+ }
+ }
+ if($rule['status'] > 0) {
+ $menuRules[] = $rule;
+ }
+ }
+ }
+ return $menuRules;
+ }
+
+ /**
+ * 内容栏目菜单
+ * @param $cates
+ * @param int $parent_id
+ * @return array
+ */
+ private function getMenus($cates,$parentId=0)
+ {
+ $menus = [];
+ foreach($cates as $cate){
+ if($cate['parent_id'] == $parentId && $cate['state'] == 1){
+ $children = $this->getMenus($cates,$cate['id']);
+ if(!empty($children)){
+ $cate['children'] = $children;
+ }
+ if(!empty($cate['children']) || !empty($cate['manager'])){
+ $menus[] = $cate;
+ }
+ }
+ }
+ return $menus;
+ }
+}
diff --git a/app/widget/manager/Upload.php b/app/widget/manager/Upload.php
new file mode 100644
index 0000000..5181ec1
--- /dev/null
+++ b/app/widget/manager/Upload.php
@@ -0,0 +1,111 @@
+ $src,
+ 'append' => $append
+ ];
+ return View::assign($data)->fetch('manager/widget/video');
+ }
+
+ //图片(layui自带上传控件),若同一页面内徐亚加载多层本上传控件则需要传不同的$append来区分控件ID
+ public function image($src = '', $append = '', $imgSize = 0, $thumb = 0, $fullName = '')
+ {
+ $data = [
+ 'src' => $src,
+ 'append' => !empty($append) ? $fullName : '',//预防只传了fullName
+ 'imgSize' => $imgSize,
+ 'fieldName' => $fullName ?: 'img'.$append,//最终后端接收数据的name
+ 'thumb' => $thumb,
+ ];
+ return View::assign($data)->fetch('manager/widget/image');
+ }
+
+ //上传文件,目前在文章中添加附件
+ public function files($files = [], $num = 10, $append = '')
+ {
+ if (!empty($files) && $files == 'null') {
+ $files = [];
+ }
+ $data = [
+ 'files' => $files,
+ 'append' => $append,
+ 'num' => $num,
+ ];
+ return View::assign($data)->fetch('manager/widget/files');
+ }
+
+ /**
+ * 水印图片上传
+ * milo
+ * 2018-01-13
+ */
+ public function mark($src = '')
+ {
+ return View::assign(['src' => $src])->fetch('manager/widget/mark');
+ }
+
+ /**
+ * layui组图上传
+ * milo
+ */
+ public function multi_bak($imgs = [], $num = 10, $append = '', $imgSize = '')
+ {
+ if (!empty($imgs) && $imgs == 'null') {
+ $imgs = [];
+ }
+ $data = [
+ 'imgs' => $imgs,
+ 'append' => $append,
+ 'imgSize' => $imgSize,
+ 'num' => $num
+ ];
+ return View::assign($data)->fetch('manager/widget/multi_bak');
+ }
+
+ /**
+ * @param array $imgs 需要展示的图片列表
+ * @param int $num 最大数量
+ * @param string $append 追加名称,用于多组图片时区别名称 当$fullName存在时不生效
+ * @param string $imgSize 图片建议尺寸
+ * @param string $fullName 组图name 传递到后台程序的名字 如image 后台则收到image的数组
+ * @param array|bool $fields 图片额外树形字段,默认有alt link desc time 若此处设置后将替换默认值。为false则不填写 格式如下
+ * ['alt' => 'alt', 'desc' => '描述,选填', 'link' => '链接,选填']
+ * @return string
+ * @throws Exception
+ */
+ public function multi(array $imgs = [], int $num = 10, string $append = 'img', string $imgSize = '', string $fullName = '', $fields = []): string
+ {
+ if (!empty($imgs) && $imgs == 'null') {
+ $imgs = [];
+ }
+
+ if (empty($fields)) {
+ $fields = $fields === false ? [] : [
+ 'alt' => 'alt',
+ 'desc' => '描述',
+ 'link' => '链接,选填',
+ 'time' => '日期,选填',
+ ];
+ }
+
+ $data = [
+ 'imgs' => $imgs,
+ 'append' => $append ?: $fullName,//预防只传了fullName
+ 'imgSize' => $imgSize,
+ 'fields' => $fields,
+ 'fieldName' => $fullName ?: 'img'.$append,//最终后端接收数据的name
+ 'num' => $num
+ ];
+ return View::assign($data)->fetch('manager/widget/multi');
+ }
+}
diff --git a/build.example.php b/build.example.php
new file mode 100644
index 0000000..0f2222f
--- /dev/null
+++ b/build.example.php
@@ -0,0 +1,26 @@
+
+// +----------------------------------------------------------------------
+
+/**
+ * php think build 自动生成应用的目录结构的定义示例
+ */
+return [
+ // 需要自动创建的文件
+ '__file__' => [],
+ // 需要自动创建的目录
+ '__dir__' => ['controller', 'model', 'view'],
+ // 需要自动创建的控制器
+ 'controller' => ['Index'],
+ // 需要自动创建的模型
+ 'model' => ['User'],
+ // 需要自动创建的模板
+ 'view' => ['index/index'],
+];
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..ef6cb1e
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,63 @@
+{
+ "name": "topthink/think",
+ "description": "the new thinkphp framework",
+ "type": "project",
+ "keywords": [
+ "framework",
+ "thinkphp",
+ "ORM"
+ ],
+ "homepage": "http://thinkphp.cn/",
+ "license": "Apache-2.0",
+ "authors": [
+ {
+ "name": "liu21st",
+ "email": "liu21st@gmail.com"
+ }
+ ],
+ "require": {
+ "php": ">=7.4.0",
+ "topthink/framework": "^6.0.0",
+ "topthink/think-orm": "^2.0",
+ "topthink/think-view": "^1.0",
+ "topthink/think-image": "^1.0",
+ "lcobucci/jwt": "^4.1",
+ "overtrue/socialite": "3.x",
+ "overtrue/wechat": "~5.0",
+ "ext-json": "*",
+ "yansongda/pay": "^2.10",
+ "ext-curl": "*",
+ "topthink/think-queue": "^3.0",
+ "topthink/think-captcha": "^3.0",
+ "casbin/think-authz": "^1.2",
+ "phpoffice/phpspreadsheet": "^1.17",
+ "endroid/qr-code": "^4.2",
+ "aliyuncs/oss-sdk-php": "^2.4",
+ "ext-bcmath": "*",
+ "intervention/image": "^2.7"
+ },
+ "require-dev": {
+ "symfony/var-dumper": "^4.2",
+ "topthink/think-trace":"^1.0"
+ },
+ "autoload": {
+ "psr-4": {
+ "app\\": "app"
+ },
+ "psr-0": {
+ "": "extend/"
+ }
+ },
+ "config": {
+ "preferred-install": "dist",
+ "allow-plugins": {
+ "easywechat-composer/easywechat-composer": true
+ }
+ },
+ "scripts": {
+ "post-autoload-dump": [
+ "@php think service:discover",
+ "@php think vendor:publish"
+ ]
+ }
+}
diff --git a/composer.lock b/composer.lock
new file mode 100644
index 0000000..03e0663
--- /dev/null
+++ b/composer.lock
@@ -0,0 +1,4986 @@
+{
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+ "This file is @generated automatically"
+ ],
+ "content-hash": "bc14049f509e9c8a07504af2702db3cc",
+ "packages": [
+ {
+ "name": "aliyuncs/oss-sdk-php",
+ "version": "v2.4.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/aliyun/aliyun-oss-php-sdk.git",
+ "reference": "4ccead614915ee6685bf30016afb01aabd347e46"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/aliyun/aliyun-oss-php-sdk/zipball/4ccead614915ee6685bf30016afb01aabd347e46",
+ "reference": "4ccead614915ee6685bf30016afb01aabd347e46",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=5.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "*",
+ "satooshi/php-coveralls": "*"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "OSS\\": "src/OSS"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Aliyuncs",
+ "homepage": "http://www.aliyun.com"
+ }
+ ],
+ "description": "Aliyun OSS SDK for PHP",
+ "homepage": "http://www.aliyun.com/product/oss/",
+ "support": {
+ "issues": "https://github.com/aliyun/aliyun-oss-php-sdk/issues",
+ "source": "https://github.com/aliyun/aliyun-oss-php-sdk/tree/v2.4.3"
+ },
+ "time": "2021-08-25T13:03:58+00:00"
+ },
+ {
+ "name": "bacon/bacon-qr-code",
+ "version": "2.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Bacon/BaconQrCode.git",
+ "reference": "f73543ac4e1def05f1a70bcd1525c8a157a1ad09"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/f73543ac4e1def05f1a70bcd1525c8a157a1ad09",
+ "reference": "f73543ac4e1def05f1a70bcd1525c8a157a1ad09",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "dasprid/enum": "^1.0.3",
+ "ext-iconv": "*",
+ "php": "^7.1 || ^8.0"
+ },
+ "require-dev": {
+ "phly/keep-a-changelog": "^1.4",
+ "phpunit/phpunit": "^7 | ^8 | ^9",
+ "squizlabs/php_codesniffer": "^3.4"
+ },
+ "suggest": {
+ "ext-imagick": "to generate QR code images"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "BaconQrCode\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-2-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Ben Scholzen 'DASPRiD'",
+ "email": "mail@dasprids.de",
+ "homepage": "https://dasprids.de/",
+ "role": "Developer"
+ }
+ ],
+ "description": "BaconQrCode is a QR code generator for PHP.",
+ "homepage": "https://github.com/Bacon/BaconQrCode",
+ "support": {
+ "issues": "https://github.com/Bacon/BaconQrCode/issues",
+ "source": "https://github.com/Bacon/BaconQrCode/tree/2.0.4"
+ },
+ "time": "2021-06-18T13:26:35+00:00"
+ },
+ {
+ "name": "casbin/casbin",
+ "version": "v3.20.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-casbin/php-casbin.git",
+ "reference": "f433e3d6dd5d803d0e19c088b6bf6c9262b645c1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-casbin/php-casbin/zipball/f433e3d6dd5d803d0e19c088b6bf6c9262b645c1",
+ "reference": "f433e3d6dd5d803d0e19c088b6bf6c9262b645c1",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.1.0",
+ "s1lentium/iptools": "^1.1",
+ "symfony/expression-language": "^3.4|^4.0|^5.0|^6.0"
+ },
+ "require-dev": {
+ "mockery/mockery": "^1.2",
+ "php-coveralls/php-coveralls": "^2.1",
+ "phpstan/phpstan": "^1.2",
+ "phpunit/phpunit": "~7.0|~8.0|~9.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Casbin\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "TechLee",
+ "email": "techlee@qq.com"
+ }
+ ],
+ "description": "a powerful and efficient open-source access control library for php projects.",
+ "keywords": [
+ "abac",
+ "access control",
+ "acl",
+ "authorization",
+ "casbin",
+ "permission",
+ "rbac"
+ ],
+ "support": {
+ "issues": "https://github.com/php-casbin/php-casbin/issues",
+ "source": "https://github.com/php-casbin/php-casbin/tree/v3.20.0"
+ },
+ "funding": [
+ {
+ "url": "https://opencollective.com/casbin",
+ "type": "open_collective"
+ }
+ ],
+ "time": "2021-12-18T15:07:27+00:00"
+ },
+ {
+ "name": "casbin/psr3-bridge",
+ "version": "v1.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-casbin/psr3-bridge.git",
+ "reference": "846be79ddb0cbf1bcc85f0f63879cf966538ad36"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-casbin/psr3-bridge/zipball/846be79ddb0cbf1bcc85f0f63879cf966538ad36",
+ "reference": "846be79ddb0cbf1bcc85f0f63879cf966538ad36",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "casbin/casbin": "^2.0|^3.0",
+ "psr/log": "^1.1"
+ },
+ "require-dev": {
+ "mockery/mockery": "^1.2",
+ "php-coveralls/php-coveralls": "^2.1",
+ "phpunit/phpunit": "~7.0|~8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Casbin\\Bridge\\Logger\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "TechLee",
+ "email": "techlee@qq.com"
+ }
+ ],
+ "description": "This library provides a PSR-3 compliant bridge for Casbin Logger.",
+ "keywords": [
+ "acl",
+ "casbin",
+ "logger",
+ "permission",
+ "psr-3",
+ "rbac"
+ ],
+ "support": {
+ "issues": "https://github.com/php-casbin/psr3-bridge/issues",
+ "source": "https://github.com/php-casbin/psr3-bridge/tree/v1.3.0"
+ },
+ "time": "2020-12-24T09:24:22+00:00"
+ },
+ {
+ "name": "casbin/think-authz",
+ "version": "v1.5.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-casbin/think-authz.git",
+ "reference": "71b7ff5f318496b617ae976d52a7559615674d7d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-casbin/think-authz/zipball/71b7ff5f318496b617ae976d52a7559615674d7d",
+ "reference": "71b7ff5f318496b617ae976d52a7559615674d7d",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "casbin/casbin": "~3.0",
+ "casbin/psr3-bridge": "^1.1",
+ "topthink/framework": "^6.0.0",
+ "topthink/think-migration": "^3.0"
+ },
+ "require-dev": {
+ "php-coveralls/php-coveralls": "^2.1",
+ "phpunit/phpunit": "~7.0|~8.0|~9.0",
+ "topthink/think": "^6.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "think": {
+ "services": [
+ "tauthz\\TauthzService"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "tauthz\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "TechLee",
+ "email": "techlee@qq.com"
+ }
+ ],
+ "description": "An authorization library that supports access control models like ACL, RBAC, ABAC in ThinkPHP 6. ",
+ "keywords": [
+ "abac",
+ "access-control",
+ "acl",
+ "authorization",
+ "authz",
+ "casbin",
+ "permission",
+ "rbac",
+ "thinkphp"
+ ],
+ "support": {
+ "issues": "https://github.com/php-casbin/think-authz/issues",
+ "source": "https://github.com/php-casbin/think-authz/tree/v1.5.2"
+ },
+ "time": "2021-11-05T02:08:00+00:00"
+ },
+ {
+ "name": "dasprid/enum",
+ "version": "1.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/DASPRiD/Enum.git",
+ "reference": "5abf82f213618696dda8e3bf6f64dd042d8542b2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/5abf82f213618696dda8e3bf6f64dd042d8542b2",
+ "reference": "5abf82f213618696dda8e3bf6f64dd042d8542b2",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^7 | ^8 | ^9",
+ "squizlabs/php_codesniffer": "^3.4"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "DASPRiD\\Enum\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-2-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Ben Scholzen 'DASPRiD'",
+ "email": "mail@dasprids.de",
+ "homepage": "https://dasprids.de/",
+ "role": "Developer"
+ }
+ ],
+ "description": "PHP 7.1 enum implementation",
+ "keywords": [
+ "enum",
+ "map"
+ ],
+ "support": {
+ "issues": "https://github.com/DASPRiD/Enum/issues",
+ "source": "https://github.com/DASPRiD/Enum/tree/1.0.3"
+ },
+ "time": "2020-10-02T16:03:48+00:00"
+ },
+ {
+ "name": "easywechat-composer/easywechat-composer",
+ "version": "1.4.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/mingyoung/easywechat-composer.git",
+ "reference": "3fc6a7ab6d3853c0f4e2922539b56cc37ef361cd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/mingyoung/easywechat-composer/zipball/3fc6a7ab6d3853c0f4e2922539b56cc37ef361cd",
+ "reference": "3fc6a7ab6d3853c0f4e2922539b56cc37ef361cd",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "composer-plugin-api": "^1.0 || ^2.0",
+ "php": ">=7.0"
+ },
+ "require-dev": {
+ "composer/composer": "^1.0 || ^2.0",
+ "phpunit/phpunit": "^6.5 || ^7.0"
+ },
+ "type": "composer-plugin",
+ "extra": {
+ "class": "EasyWeChatComposer\\Plugin"
+ },
+ "autoload": {
+ "psr-4": {
+ "EasyWeChatComposer\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "张铭阳",
+ "email": "mingyoungcheung@gmail.com"
+ }
+ ],
+ "description": "The composer plugin for EasyWeChat",
+ "support": {
+ "issues": "https://github.com/mingyoung/easywechat-composer/issues",
+ "source": "https://github.com/mingyoung/easywechat-composer/tree/1.4.1"
+ },
+ "time": "2021-07-05T04:03:22+00:00"
+ },
+ {
+ "name": "endroid/qr-code",
+ "version": "4.4.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/endroid/qr-code.git",
+ "reference": "361b43bbdfa4360442369d0a236e7d8756160523"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/endroid/qr-code/zipball/361b43bbdfa4360442369d0a236e7d8756160523",
+ "reference": "361b43bbdfa4360442369d0a236e7d8756160523",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "bacon/bacon-qr-code": "^2.0",
+ "php": "^7.4||^8.0"
+ },
+ "require-dev": {
+ "endroid/quality": "dev-master",
+ "ext-gd": "*",
+ "khanamiryan/qrcode-detector-decoder": "^1.0.4",
+ "setasign/fpdf": "^1.8.2"
+ },
+ "suggest": {
+ "ext-gd": "Enables you to write PNG images",
+ "khanamiryan/qrcode-detector-decoder": "Enables you to use the image validator",
+ "roave/security-advisories": "Makes sure package versions with known security issues are not installed",
+ "setasign/fpdf": "Enables you to use the PDF writer"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Endroid\\QrCode\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jeroen van den Enden",
+ "email": "info@endroid.nl"
+ }
+ ],
+ "description": "Endroid QR Code",
+ "homepage": "https://github.com/endroid/qr-code",
+ "keywords": [
+ "code",
+ "endroid",
+ "php",
+ "qr",
+ "qrcode"
+ ],
+ "support": {
+ "issues": "https://github.com/endroid/qr-code/issues",
+ "source": "https://github.com/endroid/qr-code/tree/4.4.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/endroid",
+ "type": "github"
+ }
+ ],
+ "time": "2021-12-13T11:01:54+00:00"
+ },
+ {
+ "name": "ezyang/htmlpurifier",
+ "version": "v4.14.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ezyang/htmlpurifier.git",
+ "reference": "12ab42bd6e742c70c0a52f7b82477fcd44e64b75"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/12ab42bd6e742c70c0a52f7b82477fcd44e64b75",
+ "reference": "12ab42bd6e742c70c0a52f7b82477fcd44e64b75",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=5.2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-0": {
+ "HTMLPurifier": "library/"
+ },
+ "files": [
+ "library/HTMLPurifier.composer.php"
+ ],
+ "exclude-from-classmap": [
+ "/library/HTMLPurifier/Language/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "LGPL-2.1-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Edward Z. Yang",
+ "email": "admin@htmlpurifier.org",
+ "homepage": "http://ezyang.com"
+ }
+ ],
+ "description": "Standards compliant HTML filter written in PHP",
+ "homepage": "http://htmlpurifier.org/",
+ "keywords": [
+ "html"
+ ],
+ "support": {
+ "issues": "https://github.com/ezyang/htmlpurifier/issues",
+ "source": "https://github.com/ezyang/htmlpurifier/tree/v4.14.0"
+ },
+ "time": "2021-12-25T01:21:49+00:00"
+ },
+ {
+ "name": "guzzlehttp/guzzle",
+ "version": "7.4.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/guzzle.git",
+ "reference": "ee0a041b1760e6a53d2a39c8c34115adc2af2c79"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/guzzle/zipball/ee0a041b1760e6a53d2a39c8c34115adc2af2c79",
+ "reference": "ee0a041b1760e6a53d2a39c8c34115adc2af2c79",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "ext-json": "*",
+ "guzzlehttp/promises": "^1.5",
+ "guzzlehttp/psr7": "^1.8.3 || ^2.1",
+ "php": "^7.2.5 || ^8.0",
+ "psr/http-client": "^1.0",
+ "symfony/deprecation-contracts": "^2.2 || ^3.0"
+ },
+ "provide": {
+ "psr/http-client-implementation": "1.0"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.4.1",
+ "ext-curl": "*",
+ "php-http/client-integration-tests": "^3.0",
+ "phpunit/phpunit": "^8.5.5 || ^9.3.5",
+ "psr/log": "^1.1 || ^2.0 || ^3.0"
+ },
+ "suggest": {
+ "ext-curl": "Required for CURL handler support",
+ "ext-intl": "Required for Internationalized Domain Name (IDN) support",
+ "psr/log": "Required for using the Log middleware"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "7.4-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\": "src/"
+ },
+ "files": [
+ "src/functions_include.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "Jeremy Lindblom",
+ "email": "jeremeamia@gmail.com",
+ "homepage": "https://github.com/jeremeamia"
+ },
+ {
+ "name": "George Mponos",
+ "email": "gmponos@gmail.com",
+ "homepage": "https://github.com/gmponos"
+ },
+ {
+ "name": "Tobias Nyholm",
+ "email": "tobias.nyholm@gmail.com",
+ "homepage": "https://github.com/Nyholm"
+ },
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com",
+ "homepage": "https://github.com/sagikazarmark"
+ },
+ {
+ "name": "Tobias Schultze",
+ "email": "webmaster@tubo-world.de",
+ "homepage": "https://github.com/Tobion"
+ }
+ ],
+ "description": "Guzzle is a PHP HTTP client library",
+ "keywords": [
+ "client",
+ "curl",
+ "framework",
+ "http",
+ "http client",
+ "psr-18",
+ "psr-7",
+ "rest",
+ "web service"
+ ],
+ "support": {
+ "issues": "https://github.com/guzzle/guzzle/issues",
+ "source": "https://github.com/guzzle/guzzle/tree/7.4.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/Nyholm",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-12-06T18:43:05+00:00"
+ },
+ {
+ "name": "guzzlehttp/promises",
+ "version": "1.5.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/promises.git",
+ "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/promises/zipball/fe752aedc9fd8fcca3fe7ad05d419d32998a06da",
+ "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=5.5"
+ },
+ "require-dev": {
+ "symfony/phpunit-bridge": "^4.4 || ^5.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.5-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Promise\\": "src/"
+ },
+ "files": [
+ "src/functions_include.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "Tobias Nyholm",
+ "email": "tobias.nyholm@gmail.com",
+ "homepage": "https://github.com/Nyholm"
+ },
+ {
+ "name": "Tobias Schultze",
+ "email": "webmaster@tubo-world.de",
+ "homepage": "https://github.com/Tobion"
+ }
+ ],
+ "description": "Guzzle promises library",
+ "keywords": [
+ "promise"
+ ],
+ "support": {
+ "issues": "https://github.com/guzzle/promises/issues",
+ "source": "https://github.com/guzzle/promises/tree/1.5.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/Nyholm",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-10-22T20:56:57+00:00"
+ },
+ {
+ "name": "guzzlehttp/psr7",
+ "version": "2.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/psr7.git",
+ "reference": "089edd38f5b8abba6cb01567c2a8aaa47cec4c72"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/psr7/zipball/089edd38f5b8abba6cb01567c2a8aaa47cec4c72",
+ "reference": "089edd38f5b8abba6cb01567c2a8aaa47cec4c72",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": "^7.2.5 || ^8.0",
+ "psr/http-factory": "^1.0",
+ "psr/http-message": "^1.0",
+ "ralouphie/getallheaders": "^3.0"
+ },
+ "provide": {
+ "psr/http-factory-implementation": "1.0",
+ "psr/http-message-implementation": "1.0"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.4.1",
+ "http-interop/http-factory-tests": "^0.9",
+ "phpunit/phpunit": "^8.5.8 || ^9.3.10"
+ },
+ "suggest": {
+ "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.1-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Psr7\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "George Mponos",
+ "email": "gmponos@gmail.com",
+ "homepage": "https://github.com/gmponos"
+ },
+ {
+ "name": "Tobias Nyholm",
+ "email": "tobias.nyholm@gmail.com",
+ "homepage": "https://github.com/Nyholm"
+ },
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com",
+ "homepage": "https://github.com/sagikazarmark"
+ },
+ {
+ "name": "Tobias Schultze",
+ "email": "webmaster@tubo-world.de",
+ "homepage": "https://github.com/Tobion"
+ },
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com",
+ "homepage": "https://sagikazarmark.hu"
+ }
+ ],
+ "description": "PSR-7 message implementation that also provides common utility methods",
+ "keywords": [
+ "http",
+ "message",
+ "psr-7",
+ "request",
+ "response",
+ "stream",
+ "uri",
+ "url"
+ ],
+ "support": {
+ "issues": "https://github.com/guzzle/psr7/issues",
+ "source": "https://github.com/guzzle/psr7/tree/2.1.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/Nyholm",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-10-06T17:43:30+00:00"
+ },
+ {
+ "name": "intervention/image",
+ "version": "2.7.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Intervention/image.git",
+ "reference": "744ebba495319501b873a4e48787759c72e3fb8c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Intervention/image/zipball/744ebba495319501b873a4e48787759c72e3fb8c",
+ "reference": "744ebba495319501b873a4e48787759c72e3fb8c",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "ext-fileinfo": "*",
+ "guzzlehttp/psr7": "~1.1 || ^2.0",
+ "php": ">=5.4.0"
+ },
+ "require-dev": {
+ "mockery/mockery": "~0.9.2",
+ "phpunit/phpunit": "^4.8 || ^5.7 || ^7.5.15"
+ },
+ "suggest": {
+ "ext-gd": "to use GD library based image processing.",
+ "ext-imagick": "to use Imagick based image processing.",
+ "intervention/imagecache": "Caching extension for the Intervention Image library"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.4-dev"
+ },
+ "laravel": {
+ "providers": [
+ "Intervention\\Image\\ImageServiceProvider"
+ ],
+ "aliases": {
+ "Image": "Intervention\\Image\\Facades\\Image"
+ }
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Intervention\\Image\\": "src/Intervention/Image"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Oliver Vogel",
+ "email": "oliver@olivervogel.com",
+ "homepage": "http://olivervogel.com/"
+ }
+ ],
+ "description": "Image handling and manipulation library with support for Laravel integration",
+ "homepage": "http://image.intervention.io/",
+ "keywords": [
+ "gd",
+ "image",
+ "imagick",
+ "laravel",
+ "thumbnail",
+ "watermark"
+ ],
+ "support": {
+ "issues": "https://github.com/Intervention/image/issues",
+ "source": "https://github.com/Intervention/image/tree/2.7.1"
+ },
+ "funding": [
+ {
+ "url": "https://www.paypal.me/interventionphp",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/Intervention",
+ "type": "github"
+ }
+ ],
+ "time": "2021-12-16T16:49:26+00:00"
+ },
+ {
+ "name": "lcobucci/clock",
+ "version": "2.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/lcobucci/clock.git",
+ "reference": "353d83fe2e6ae95745b16b3d911813df6a05bfb3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/lcobucci/clock/zipball/353d83fe2e6ae95745b16b3d911813df6a05bfb3",
+ "reference": "353d83fe2e6ae95745b16b3d911813df6a05bfb3",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": "^7.4 || ^8.0"
+ },
+ "require-dev": {
+ "infection/infection": "^0.17",
+ "lcobucci/coding-standard": "^6.0",
+ "phpstan/extension-installer": "^1.0",
+ "phpstan/phpstan": "^0.12",
+ "phpstan/phpstan-deprecation-rules": "^0.12",
+ "phpstan/phpstan-phpunit": "^0.12",
+ "phpstan/phpstan-strict-rules": "^0.12",
+ "phpunit/php-code-coverage": "9.1.4",
+ "phpunit/phpunit": "9.3.7"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Lcobucci\\Clock\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Luís Cobucci",
+ "email": "lcobucci@gmail.com"
+ }
+ ],
+ "description": "Yet another clock abstraction",
+ "support": {
+ "issues": "https://github.com/lcobucci/clock/issues",
+ "source": "https://github.com/lcobucci/clock/tree/2.0.x"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/lcobucci",
+ "type": "github"
+ },
+ {
+ "url": "https://www.patreon.com/lcobucci",
+ "type": "patreon"
+ }
+ ],
+ "time": "2020-08-27T18:56:02+00:00"
+ },
+ {
+ "name": "lcobucci/jwt",
+ "version": "4.1.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/lcobucci/jwt.git",
+ "reference": "fe2d89f2eaa7087af4aa166c6f480ef04e000582"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/lcobucci/jwt/zipball/fe2d89f2eaa7087af4aa166c6f480ef04e000582",
+ "reference": "fe2d89f2eaa7087af4aa166c6f480ef04e000582",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "ext-hash": "*",
+ "ext-json": "*",
+ "ext-mbstring": "*",
+ "ext-openssl": "*",
+ "ext-sodium": "*",
+ "lcobucci/clock": "^2.0",
+ "php": "^7.4 || ^8.0"
+ },
+ "require-dev": {
+ "infection/infection": "^0.21",
+ "lcobucci/coding-standard": "^6.0",
+ "mikey179/vfsstream": "^1.6.7",
+ "phpbench/phpbench": "^1.0",
+ "phpstan/extension-installer": "^1.0",
+ "phpstan/phpstan": "^0.12",
+ "phpstan/phpstan-deprecation-rules": "^0.12",
+ "phpstan/phpstan-phpunit": "^0.12",
+ "phpstan/phpstan-strict-rules": "^0.12",
+ "phpunit/php-invoker": "^3.1",
+ "phpunit/phpunit": "^9.5"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Lcobucci\\JWT\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Luís Cobucci",
+ "email": "lcobucci@gmail.com",
+ "role": "Developer"
+ }
+ ],
+ "description": "A simple library to work with JSON Web Token and JSON Web Signature",
+ "keywords": [
+ "JWS",
+ "jwt"
+ ],
+ "support": {
+ "issues": "https://github.com/lcobucci/jwt/issues",
+ "source": "https://github.com/lcobucci/jwt/tree/4.1.5"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/lcobucci",
+ "type": "github"
+ },
+ {
+ "url": "https://www.patreon.com/lcobucci",
+ "type": "patreon"
+ }
+ ],
+ "time": "2021-09-28T19:34:56+00:00"
+ },
+ {
+ "name": "league/flysystem",
+ "version": "1.1.9",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/flysystem.git",
+ "reference": "094defdb4a7001845300334e7c1ee2335925ef99"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/094defdb4a7001845300334e7c1ee2335925ef99",
+ "reference": "094defdb4a7001845300334e7c1ee2335925ef99",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "ext-fileinfo": "*",
+ "league/mime-type-detection": "^1.3",
+ "php": "^7.2.5 || ^8.0"
+ },
+ "conflict": {
+ "league/flysystem-sftp": "<1.0.6"
+ },
+ "require-dev": {
+ "phpspec/prophecy": "^1.11.1",
+ "phpunit/phpunit": "^8.5.8"
+ },
+ "suggest": {
+ "ext-ftp": "Allows you to use FTP server storage",
+ "ext-openssl": "Allows you to use FTPS server storage",
+ "league/flysystem-aws-s3-v2": "Allows you to use S3 storage with AWS SDK v2",
+ "league/flysystem-aws-s3-v3": "Allows you to use S3 storage with AWS SDK v3",
+ "league/flysystem-azure": "Allows you to use Windows Azure Blob storage",
+ "league/flysystem-cached-adapter": "Flysystem adapter decorator for metadata caching",
+ "league/flysystem-eventable-filesystem": "Allows you to use EventableFilesystem",
+ "league/flysystem-rackspace": "Allows you to use Rackspace Cloud Files",
+ "league/flysystem-sftp": "Allows you to use SFTP server storage via phpseclib",
+ "league/flysystem-webdav": "Allows you to use WebDAV storage",
+ "league/flysystem-ziparchive": "Allows you to use ZipArchive adapter",
+ "spatie/flysystem-dropbox": "Allows you to use Dropbox storage",
+ "srmklive/flysystem-dropbox-v2": "Allows you to use Dropbox storage for PHP 5 applications"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.1-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "League\\Flysystem\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Frank de Jonge",
+ "email": "info@frenky.net"
+ }
+ ],
+ "description": "Filesystem abstraction: Many filesystems, one API.",
+ "keywords": [
+ "Cloud Files",
+ "WebDAV",
+ "abstraction",
+ "aws",
+ "cloud",
+ "copy.com",
+ "dropbox",
+ "file systems",
+ "files",
+ "filesystem",
+ "filesystems",
+ "ftp",
+ "rackspace",
+ "remote",
+ "s3",
+ "sftp",
+ "storage"
+ ],
+ "support": {
+ "issues": "https://github.com/thephpleague/flysystem/issues",
+ "source": "https://github.com/thephpleague/flysystem/tree/1.1.9"
+ },
+ "funding": [
+ {
+ "url": "https://offset.earth/frankdejonge",
+ "type": "other"
+ }
+ ],
+ "time": "2021-12-09T09:40:50+00:00"
+ },
+ {
+ "name": "league/flysystem-cached-adapter",
+ "version": "1.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/flysystem-cached-adapter.git",
+ "reference": "d1925efb2207ac4be3ad0c40b8277175f99ffaff"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/flysystem-cached-adapter/zipball/d1925efb2207ac4be3ad0c40b8277175f99ffaff",
+ "reference": "d1925efb2207ac4be3ad0c40b8277175f99ffaff",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "league/flysystem": "~1.0",
+ "psr/cache": "^1.0.0"
+ },
+ "require-dev": {
+ "mockery/mockery": "~0.9",
+ "phpspec/phpspec": "^3.4",
+ "phpunit/phpunit": "^5.7",
+ "predis/predis": "~1.0",
+ "tedivm/stash": "~0.12"
+ },
+ "suggest": {
+ "ext-phpredis": "Pure C implemented extension for PHP"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "League\\Flysystem\\Cached\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "frankdejonge",
+ "email": "info@frenky.net"
+ }
+ ],
+ "description": "An adapter decorator to enable meta-data caching.",
+ "support": {
+ "issues": "https://github.com/thephpleague/flysystem-cached-adapter/issues",
+ "source": "https://github.com/thephpleague/flysystem-cached-adapter/tree/master"
+ },
+ "time": "2020-07-25T15:56:04+00:00"
+ },
+ {
+ "name": "league/mime-type-detection",
+ "version": "1.9.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/mime-type-detection.git",
+ "reference": "aa70e813a6ad3d1558fc927863d47309b4c23e69"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/aa70e813a6ad3d1558fc927863d47309b4c23e69",
+ "reference": "aa70e813a6ad3d1558fc927863d47309b4c23e69",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "ext-fileinfo": "*",
+ "php": "^7.2 || ^8.0"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "^3.2",
+ "phpstan/phpstan": "^0.12.68",
+ "phpunit/phpunit": "^8.5.8 || ^9.3"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "League\\MimeTypeDetection\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Frank de Jonge",
+ "email": "info@frankdejonge.nl"
+ }
+ ],
+ "description": "Mime-type detection for Flysystem",
+ "support": {
+ "issues": "https://github.com/thephpleague/mime-type-detection/issues",
+ "source": "https://github.com/thephpleague/mime-type-detection/tree/1.9.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/frankdejonge",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/league/flysystem",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-11-21T11:48:40+00:00"
+ },
+ {
+ "name": "maennchen/zipstream-php",
+ "version": "2.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/maennchen/ZipStream-PHP.git",
+ "reference": "c4c5803cc1f93df3d2448478ef79394a5981cc58"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/c4c5803cc1f93df3d2448478ef79394a5981cc58",
+ "reference": "c4c5803cc1f93df3d2448478ef79394a5981cc58",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "myclabs/php-enum": "^1.5",
+ "php": ">= 7.1",
+ "psr/http-message": "^1.0",
+ "symfony/polyfill-mbstring": "^1.0"
+ },
+ "require-dev": {
+ "ext-zip": "*",
+ "guzzlehttp/guzzle": ">= 6.3",
+ "mikey179/vfsstream": "^1.6",
+ "phpunit/phpunit": ">= 7.5"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "ZipStream\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Paul Duncan",
+ "email": "pabs@pablotron.org"
+ },
+ {
+ "name": "Jonatan Männchen",
+ "email": "jonatan@maennchen.ch"
+ },
+ {
+ "name": "Jesse Donat",
+ "email": "donatj@gmail.com"
+ },
+ {
+ "name": "András Kolesár",
+ "email": "kolesar@kolesar.hu"
+ }
+ ],
+ "description": "ZipStream is a library for dynamically streaming dynamic zip files from PHP without writing to the disk at all on the server.",
+ "keywords": [
+ "stream",
+ "zip"
+ ],
+ "support": {
+ "issues": "https://github.com/maennchen/ZipStream-PHP/issues",
+ "source": "https://github.com/maennchen/ZipStream-PHP/tree/master"
+ },
+ "funding": [
+ {
+ "url": "https://opencollective.com/zipstream",
+ "type": "open_collective"
+ }
+ ],
+ "time": "2020-05-30T13:11:16+00:00"
+ },
+ {
+ "name": "markbaker/complex",
+ "version": "3.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/MarkBaker/PHPComplex.git",
+ "reference": "ab8bc271e404909db09ff2d5ffa1e538085c0f22"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/ab8bc271e404909db09ff2d5ffa1e538085c0f22",
+ "reference": "ab8bc271e404909db09ff2d5ffa1e538085c0f22",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "require-dev": {
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
+ "phpcompatibility/php-compatibility": "^9.0",
+ "phpunit/phpunit": "^7.0 || ^8.0 || ^9.3",
+ "squizlabs/php_codesniffer": "^3.4"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Complex\\": "classes/src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Mark Baker",
+ "email": "mark@lange.demon.co.uk"
+ }
+ ],
+ "description": "PHP Class for working with complex numbers",
+ "homepage": "https://github.com/MarkBaker/PHPComplex",
+ "keywords": [
+ "complex",
+ "mathematics"
+ ],
+ "support": {
+ "issues": "https://github.com/MarkBaker/PHPComplex/issues",
+ "source": "https://github.com/MarkBaker/PHPComplex/tree/3.0.1"
+ },
+ "time": "2021-06-29T15:32:53+00:00"
+ },
+ {
+ "name": "markbaker/matrix",
+ "version": "3.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/MarkBaker/PHPMatrix.git",
+ "reference": "c66aefcafb4f6c269510e9ac46b82619a904c576"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/c66aefcafb4f6c269510e9ac46b82619a904c576",
+ "reference": "c66aefcafb4f6c269510e9ac46b82619a904c576",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": "^7.1 || ^8.0"
+ },
+ "require-dev": {
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
+ "phpcompatibility/php-compatibility": "^9.0",
+ "phpdocumentor/phpdocumentor": "2.*",
+ "phploc/phploc": "^4.0",
+ "phpmd/phpmd": "2.*",
+ "phpunit/phpunit": "^7.0 || ^8.0 || ^9.3",
+ "sebastian/phpcpd": "^4.0",
+ "squizlabs/php_codesniffer": "^3.4"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Matrix\\": "classes/src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Mark Baker",
+ "email": "mark@demon-angel.eu"
+ }
+ ],
+ "description": "PHP Class for working with matrices",
+ "homepage": "https://github.com/MarkBaker/PHPMatrix",
+ "keywords": [
+ "mathematics",
+ "matrix",
+ "vector"
+ ],
+ "support": {
+ "issues": "https://github.com/MarkBaker/PHPMatrix/issues",
+ "source": "https://github.com/MarkBaker/PHPMatrix/tree/3.0.0"
+ },
+ "time": "2021-07-01T19:01:15+00:00"
+ },
+ {
+ "name": "monolog/monolog",
+ "version": "2.3.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Seldaek/monolog.git",
+ "reference": "fd4380d6fc37626e2f799f29d91195040137eba9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Seldaek/monolog/zipball/fd4380d6fc37626e2f799f29d91195040137eba9",
+ "reference": "fd4380d6fc37626e2f799f29d91195040137eba9",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.2",
+ "psr/log": "^1.0.1 || ^2.0 || ^3.0"
+ },
+ "provide": {
+ "psr/log-implementation": "1.0.0 || 2.0.0 || 3.0.0"
+ },
+ "require-dev": {
+ "aws/aws-sdk-php": "^2.4.9 || ^3.0",
+ "doctrine/couchdb": "~1.0@dev",
+ "elasticsearch/elasticsearch": "^7",
+ "graylog2/gelf-php": "^1.4.2",
+ "mongodb/mongodb": "^1.8",
+ "php-amqplib/php-amqplib": "~2.4 || ^3",
+ "php-console/php-console": "^3.1.3",
+ "phpspec/prophecy": "^1.6.1",
+ "phpstan/phpstan": "^0.12.91",
+ "phpunit/phpunit": "^8.5",
+ "predis/predis": "^1.1",
+ "rollbar/rollbar": "^1.3",
+ "ruflin/elastica": ">=0.90@dev",
+ "swiftmailer/swiftmailer": "^5.3|^6.0"
+ },
+ "suggest": {
+ "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
+ "doctrine/couchdb": "Allow sending log messages to a CouchDB server",
+ "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client",
+ "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
+ "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler",
+ "ext-mbstring": "Allow to work properly with unicode symbols",
+ "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)",
+ "ext-openssl": "Required to send log messages using SSL",
+ "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)",
+ "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
+ "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)",
+ "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib",
+ "php-console/php-console": "Allow sending log messages to Google Chrome",
+ "rollbar/rollbar": "Allow sending log messages to Rollbar",
+ "ruflin/elastica": "Allow sending log messages to an Elastic Search server"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "2.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Monolog\\": "src/Monolog"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "https://seld.be"
+ }
+ ],
+ "description": "Sends your logs to files, sockets, inboxes, databases and various web services",
+ "homepage": "https://github.com/Seldaek/monolog",
+ "keywords": [
+ "log",
+ "logging",
+ "psr-3"
+ ],
+ "support": {
+ "issues": "https://github.com/Seldaek/monolog/issues",
+ "source": "https://github.com/Seldaek/monolog/tree/2.3.5"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/Seldaek",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/monolog/monolog",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-10-01T21:08:31+00:00"
+ },
+ {
+ "name": "myclabs/php-enum",
+ "version": "1.8.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/myclabs/php-enum.git",
+ "reference": "b942d263c641ddb5190929ff840c68f78713e937"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/myclabs/php-enum/zipball/b942d263c641ddb5190929ff840c68f78713e937",
+ "reference": "b942d263c641ddb5190929ff840c68f78713e937",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "ext-json": "*",
+ "php": "^7.3 || ^8.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.5",
+ "squizlabs/php_codesniffer": "1.*",
+ "vimeo/psalm": "^4.6.2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "MyCLabs\\Enum\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP Enum contributors",
+ "homepage": "https://github.com/myclabs/php-enum/graphs/contributors"
+ }
+ ],
+ "description": "PHP Enum implementation",
+ "homepage": "http://github.com/myclabs/php-enum",
+ "keywords": [
+ "enum"
+ ],
+ "support": {
+ "issues": "https://github.com/myclabs/php-enum/issues",
+ "source": "https://github.com/myclabs/php-enum/tree/1.8.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/mnapoli",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/myclabs/php-enum",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-07-05T08:18:36+00:00"
+ },
+ {
+ "name": "nesbot/carbon",
+ "version": "2.55.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/briannesbitt/Carbon.git",
+ "reference": "8c2a18ce3e67c34efc1b29f64fe61304368259a2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/8c2a18ce3e67c34efc1b29f64fe61304368259a2",
+ "reference": "8c2a18ce3e67c34efc1b29f64fe61304368259a2",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "ext-json": "*",
+ "php": "^7.1.8 || ^8.0",
+ "symfony/polyfill-mbstring": "^1.0",
+ "symfony/polyfill-php80": "^1.16",
+ "symfony/translation": "^3.4 || ^4.0 || ^5.0 || ^6.0"
+ },
+ "require-dev": {
+ "doctrine/dbal": "^2.0 || ^3.0",
+ "doctrine/orm": "^2.7",
+ "friendsofphp/php-cs-fixer": "^3.0",
+ "kylekatarnls/multi-tester": "^2.0",
+ "phpmd/phpmd": "^2.9",
+ "phpstan/extension-installer": "^1.0",
+ "phpstan/phpstan": "^0.12.54",
+ "phpunit/phpunit": "^7.5.20 || ^8.5.14",
+ "squizlabs/php_codesniffer": "^3.4"
+ },
+ "bin": [
+ "bin/carbon"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-3.x": "3.x-dev",
+ "dev-master": "2.x-dev"
+ },
+ "laravel": {
+ "providers": [
+ "Carbon\\Laravel\\ServiceProvider"
+ ]
+ },
+ "phpstan": {
+ "includes": [
+ "extension.neon"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Carbon\\": "src/Carbon/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Brian Nesbitt",
+ "email": "brian@nesbot.com",
+ "homepage": "https://markido.com"
+ },
+ {
+ "name": "kylekatarnls",
+ "homepage": "https://github.com/kylekatarnls"
+ }
+ ],
+ "description": "An API extension for DateTime that supports 281 different languages.",
+ "homepage": "https://carbon.nesbot.com",
+ "keywords": [
+ "date",
+ "datetime",
+ "time"
+ ],
+ "support": {
+ "docs": "https://carbon.nesbot.com/docs",
+ "issues": "https://github.com/briannesbitt/Carbon/issues",
+ "source": "https://github.com/briannesbitt/Carbon"
+ },
+ "funding": [
+ {
+ "url": "https://opencollective.com/Carbon",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-12-03T14:59:52+00:00"
+ },
+ {
+ "name": "overtrue/socialite",
+ "version": "3.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/overtrue/socialite.git",
+ "reference": "1607bc8aa95235b1538b3dc4a36324ee2cdf0df5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/overtrue/socialite/zipball/1607bc8aa95235b1538b3dc4a36324ee2cdf0df5",
+ "reference": "1607bc8aa95235b1538b3dc4a36324ee2cdf0df5",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "ext-json": "*",
+ "ext-openssl": "*",
+ "guzzlehttp/guzzle": "~6.0|~7.0",
+ "php": ">=7.4",
+ "symfony/http-foundation": "^5.0",
+ "symfony/psr-http-message-bridge": "^2.0"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "^3.0",
+ "mockery/mockery": "~1.2",
+ "phpunit/phpunit": "~9.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Overtrue\\Socialite\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "overtrue",
+ "email": "anzhengchao@gmail.com"
+ }
+ ],
+ "description": "A collection of OAuth 2 packages.",
+ "keywords": [
+ "Feishu",
+ "login",
+ "oauth",
+ "qcloud",
+ "qq",
+ "social",
+ "wechat",
+ "weibo"
+ ],
+ "support": {
+ "issues": "https://github.com/overtrue/socialite/issues",
+ "source": "https://github.com/overtrue/socialite/tree/3.5.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/overtrue",
+ "type": "github"
+ }
+ ],
+ "time": "2021-11-24T03:52:33+00:00"
+ },
+ {
+ "name": "overtrue/wechat",
+ "version": "5.12.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/w7corp/easywechat.git",
+ "reference": "9fb3e56e1522c46a648d05edec7970b0b3447d5c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/w7corp/easywechat/zipball/9fb3e56e1522c46a648d05edec7970b0b3447d5c",
+ "reference": "9fb3e56e1522c46a648d05edec7970b0b3447d5c",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "easywechat-composer/easywechat-composer": "^1.1",
+ "ext-fileinfo": "*",
+ "ext-libxml": "*",
+ "ext-openssl": "*",
+ "ext-simplexml": "*",
+ "guzzlehttp/guzzle": "^6.2 || ^7.0",
+ "monolog/monolog": "^1.22 || ^2.0",
+ "overtrue/socialite": "^3.2",
+ "php": ">=7.4",
+ "pimple/pimple": "^3.0",
+ "psr/simple-cache": "^1.0",
+ "symfony/cache": "^3.3 || ^4.3 || ^5.0",
+ "symfony/event-dispatcher": "^4.3 || ^5.0",
+ "symfony/http-foundation": "^2.7 || ^3.0 || ^4.0 || ^5.0",
+ "symfony/psr-http-message-bridge": "^0.3 || ^1.0 || ^2.0"
+ },
+ "require-dev": {
+ "brainmaestro/composer-git-hooks": "^2.7",
+ "dms/phpunit-arraysubset-asserts": "^0.2.0",
+ "friendsofphp/php-cs-fixer": "^2.15",
+ "mikey179/vfsstream": "^1.6",
+ "mockery/mockery": "^1.2.3",
+ "phpstan/phpstan": "^0.12.0",
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "hooks": {
+ "pre-commit": [
+ "composer test",
+ "composer fix-style"
+ ],
+ "pre-push": [
+ "composer test",
+ "composer fix-style"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "EasyWeChat\\": "src/"
+ },
+ "files": [
+ "src/Kernel/Support/Helpers.php",
+ "src/Kernel/Helpers.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "overtrue",
+ "email": "anzhengchao@gmail.com"
+ }
+ ],
+ "description": "微信SDK",
+ "keywords": [
+ "easywechat",
+ "sdk",
+ "wechat",
+ "weixin",
+ "weixin-sdk"
+ ],
+ "support": {
+ "issues": "https://github.com/w7corp/easywechat/issues",
+ "source": "https://github.com/w7corp/easywechat/tree/5.12.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/overtrue",
+ "type": "github"
+ }
+ ],
+ "time": "2021-12-20T08:08:37+00:00"
+ },
+ {
+ "name": "phpoffice/phpspreadsheet",
+ "version": "1.20.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHPOffice/PhpSpreadsheet.git",
+ "reference": "44436f270bb134b4a94670f3d020a85dfa0a3c02"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/44436f270bb134b4a94670f3d020a85dfa0a3c02",
+ "reference": "44436f270bb134b4a94670f3d020a85dfa0a3c02",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "ext-ctype": "*",
+ "ext-dom": "*",
+ "ext-fileinfo": "*",
+ "ext-gd": "*",
+ "ext-iconv": "*",
+ "ext-libxml": "*",
+ "ext-mbstring": "*",
+ "ext-simplexml": "*",
+ "ext-xml": "*",
+ "ext-xmlreader": "*",
+ "ext-xmlwriter": "*",
+ "ext-zip": "*",
+ "ext-zlib": "*",
+ "ezyang/htmlpurifier": "^4.13",
+ "maennchen/zipstream-php": "^2.1",
+ "markbaker/complex": "^3.0",
+ "markbaker/matrix": "^3.0",
+ "php": "^7.3 || ^8.0",
+ "psr/http-client": "^1.0",
+ "psr/http-factory": "^1.0",
+ "psr/simple-cache": "^1.0"
+ },
+ "require-dev": {
+ "dealerdirect/phpcodesniffer-composer-installer": "dev-master",
+ "dompdf/dompdf": "^1.0",
+ "friendsofphp/php-cs-fixer": "^3.2",
+ "jpgraph/jpgraph": "^4.0",
+ "mpdf/mpdf": "^8.0",
+ "phpcompatibility/php-compatibility": "^9.3",
+ "phpstan/phpstan": "^1.1",
+ "phpstan/phpstan-phpunit": "^1.0",
+ "phpunit/phpunit": "^8.5 || ^9.0",
+ "squizlabs/php_codesniffer": "^3.6",
+ "tecnickcom/tcpdf": "^6.4"
+ },
+ "suggest": {
+ "dompdf/dompdf": "Option for rendering PDF with PDF Writer (doesn't yet support PHP8)",
+ "jpgraph/jpgraph": "Option for rendering charts, or including charts with PDF or HTML Writers",
+ "mpdf/mpdf": "Option for rendering PDF with PDF Writer",
+ "tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer (doesn't yet support PHP8)"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "PhpOffice\\PhpSpreadsheet\\": "src/PhpSpreadsheet"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Maarten Balliauw",
+ "homepage": "https://blog.maartenballiauw.be"
+ },
+ {
+ "name": "Mark Baker",
+ "homepage": "https://markbakeruk.net"
+ },
+ {
+ "name": "Franck Lefevre",
+ "homepage": "https://rootslabs.net"
+ },
+ {
+ "name": "Erik Tilt"
+ },
+ {
+ "name": "Adrien Crivelli"
+ }
+ ],
+ "description": "PHPSpreadsheet - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine",
+ "homepage": "https://github.com/PHPOffice/PhpSpreadsheet",
+ "keywords": [
+ "OpenXML",
+ "excel",
+ "gnumeric",
+ "ods",
+ "php",
+ "spreadsheet",
+ "xls",
+ "xlsx"
+ ],
+ "support": {
+ "issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues",
+ "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.20.0"
+ },
+ "time": "2021-11-23T15:23:42+00:00"
+ },
+ {
+ "name": "pimple/pimple",
+ "version": "v3.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/silexphp/Pimple.git",
+ "reference": "a94b3a4db7fb774b3d78dad2315ddc07629e1bed"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/silexphp/Pimple/zipball/a94b3a4db7fb774b3d78dad2315ddc07629e1bed",
+ "reference": "a94b3a4db7fb774b3d78dad2315ddc07629e1bed",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "psr/container": "^1.1 || ^2.0"
+ },
+ "require-dev": {
+ "symfony/phpunit-bridge": "^5.4@dev"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.4.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-0": {
+ "Pimple": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ }
+ ],
+ "description": "Pimple, a simple Dependency Injection Container",
+ "homepage": "https://pimple.symfony.com",
+ "keywords": [
+ "container",
+ "dependency injection"
+ ],
+ "support": {
+ "source": "https://github.com/silexphp/Pimple/tree/v3.5.0"
+ },
+ "time": "2021-10-28T11:13:42+00:00"
+ },
+ {
+ "name": "psr/cache",
+ "version": "1.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/cache.git",
+ "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8",
+ "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Cache\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for caching libraries",
+ "keywords": [
+ "cache",
+ "psr",
+ "psr-6"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/cache/tree/master"
+ },
+ "time": "2016-08-06T20:24:11+00:00"
+ },
+ {
+ "name": "psr/container",
+ "version": "1.1.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/container.git",
+ "reference": "513e0666f7216c7459170d56df27dfcefe1689ea"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea",
+ "reference": "513e0666f7216c7459170d56df27dfcefe1689ea",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.4.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Psr\\Container\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common Container Interface (PHP FIG PSR-11)",
+ "homepage": "https://github.com/php-fig/container",
+ "keywords": [
+ "PSR-11",
+ "container",
+ "container-interface",
+ "container-interop",
+ "psr"
+ ],
+ "support": {
+ "issues": "https://github.com/php-fig/container/issues",
+ "source": "https://github.com/php-fig/container/tree/1.1.2"
+ },
+ "time": "2021-11-05T16:50:12+00:00"
+ },
+ {
+ "name": "psr/event-dispatcher",
+ "version": "1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/event-dispatcher.git",
+ "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0",
+ "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\EventDispatcher\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Standard interfaces for event handling.",
+ "keywords": [
+ "events",
+ "psr",
+ "psr-14"
+ ],
+ "support": {
+ "issues": "https://github.com/php-fig/event-dispatcher/issues",
+ "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0"
+ },
+ "time": "2019-01-08T18:20:26+00:00"
+ },
+ {
+ "name": "psr/http-client",
+ "version": "1.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-client.git",
+ "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
+ "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": "^7.0 || ^8.0",
+ "psr/http-message": "^1.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Client\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for HTTP clients",
+ "homepage": "https://github.com/php-fig/http-client",
+ "keywords": [
+ "http",
+ "http-client",
+ "psr",
+ "psr-18"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-client/tree/master"
+ },
+ "time": "2020-06-29T06:28:15+00:00"
+ },
+ {
+ "name": "psr/http-factory",
+ "version": "1.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-factory.git",
+ "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
+ "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.0.0",
+ "psr/http-message": "^1.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Message\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interfaces for PSR-7 HTTP message factories",
+ "keywords": [
+ "factory",
+ "http",
+ "message",
+ "psr",
+ "psr-17",
+ "psr-7",
+ "request",
+ "response"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-factory/tree/master"
+ },
+ "time": "2019-04-30T12:38:16+00:00"
+ },
+ {
+ "name": "psr/http-message",
+ "version": "1.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-message.git",
+ "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
+ "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Message\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for HTTP messages",
+ "homepage": "https://github.com/php-fig/http-message",
+ "keywords": [
+ "http",
+ "http-message",
+ "psr",
+ "psr-7",
+ "request",
+ "response"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-message/tree/master"
+ },
+ "time": "2016-08-06T14:39:51+00:00"
+ },
+ {
+ "name": "psr/log",
+ "version": "1.1.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/log.git",
+ "reference": "d49695b909c3b7628b6289db5479a1c204601f11"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11",
+ "reference": "d49695b909c3b7628b6289db5479a1c204601f11",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Log\\": "Psr/Log/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for logging libraries",
+ "homepage": "https://github.com/php-fig/log",
+ "keywords": [
+ "log",
+ "psr",
+ "psr-3"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/log/tree/1.1.4"
+ },
+ "time": "2021-05-03T11:20:27+00:00"
+ },
+ {
+ "name": "psr/simple-cache",
+ "version": "1.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/simple-cache.git",
+ "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
+ "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\SimpleCache\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interfaces for simple caching",
+ "keywords": [
+ "cache",
+ "caching",
+ "psr",
+ "psr-16",
+ "simple-cache"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/simple-cache/tree/master"
+ },
+ "time": "2017-10-23T01:57:42+00:00"
+ },
+ {
+ "name": "ralouphie/getallheaders",
+ "version": "3.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ralouphie/getallheaders.git",
+ "reference": "120b605dfeb996808c31b6477290a714d356e822"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
+ "reference": "120b605dfeb996808c31b6477290a714d356e822",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=5.6"
+ },
+ "require-dev": {
+ "php-coveralls/php-coveralls": "^2.1",
+ "phpunit/phpunit": "^5 || ^6.5"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/getallheaders.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ralph Khattar",
+ "email": "ralph.khattar@gmail.com"
+ }
+ ],
+ "description": "A polyfill for getallheaders.",
+ "support": {
+ "issues": "https://github.com/ralouphie/getallheaders/issues",
+ "source": "https://github.com/ralouphie/getallheaders/tree/develop"
+ },
+ "time": "2019-03-08T08:55:37+00:00"
+ },
+ {
+ "name": "s1lentium/iptools",
+ "version": "v1.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/S1lentium/IPTools.git",
+ "reference": "f6f8ab6132ca7443bd7cced1681f5066d725fd5f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/S1lentium/IPTools/zipball/f6f8ab6132ca7443bd7cced1681f5066d725fd5f",
+ "reference": "f6f8ab6132ca7443bd7cced1681f5066d725fd5f",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "ext-bcmath": "*",
+ "php": ">=5.4.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.0",
+ "satooshi/php-coveralls": "~1.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "IPTools\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Safarov Alisher",
+ "email": "alisher.safarov@outlook.com",
+ "homepage": "https://github.com/S1lentium"
+ }
+ ],
+ "description": "PHP Library for manipulating network addresses (IPv4 and IPv6)",
+ "keywords": [
+ "IP",
+ "IP-Tools",
+ "cidr",
+ "ipv4",
+ "ipv6",
+ "network",
+ "subnet"
+ ],
+ "support": {
+ "issues": "https://github.com/S1lentium/IPTools/issues",
+ "source": "https://github.com/S1lentium/IPTools/tree/master"
+ },
+ "time": "2018-09-19T06:15:53+00:00"
+ },
+ {
+ "name": "symfony/cache",
+ "version": "v5.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/cache.git",
+ "reference": "d97d6d7f46cb69968f094e329abd987d5ee17c79"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/cache/zipball/d97d6d7f46cb69968f094e329abd987d5ee17c79",
+ "reference": "d97d6d7f46cb69968f094e329abd987d5ee17c79",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "psr/cache": "^1.0|^2.0",
+ "psr/log": "^1.1|^2|^3",
+ "symfony/cache-contracts": "^1.1.7|^2",
+ "symfony/deprecation-contracts": "^2.1|^3",
+ "symfony/polyfill-php73": "^1.9",
+ "symfony/polyfill-php80": "^1.16",
+ "symfony/service-contracts": "^1.1|^2|^3",
+ "symfony/var-exporter": "^4.4|^5.0|^6.0"
+ },
+ "conflict": {
+ "doctrine/dbal": "<2.13.1",
+ "symfony/dependency-injection": "<4.4",
+ "symfony/http-kernel": "<4.4",
+ "symfony/var-dumper": "<4.4"
+ },
+ "provide": {
+ "psr/cache-implementation": "1.0|2.0",
+ "psr/simple-cache-implementation": "1.0|2.0",
+ "symfony/cache-implementation": "1.0|2.0"
+ },
+ "require-dev": {
+ "cache/integration-tests": "dev-master",
+ "doctrine/cache": "^1.6|^2.0",
+ "doctrine/dbal": "^2.13.1|^3.0",
+ "predis/predis": "^1.1",
+ "psr/simple-cache": "^1.0|^2.0",
+ "symfony/config": "^4.4|^5.0|^6.0",
+ "symfony/dependency-injection": "^4.4|^5.0|^6.0",
+ "symfony/filesystem": "^4.4|^5.0|^6.0",
+ "symfony/http-kernel": "^4.4|^5.0|^6.0",
+ "symfony/messenger": "^4.4|^5.0|^6.0",
+ "symfony/var-dumper": "^4.4|^5.0|^6.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Cache\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides an extended PSR-6, PSR-16 (and tags) implementation",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "caching",
+ "psr6"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/cache/tree/v5.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-11-23T18:51:45+00:00"
+ },
+ {
+ "name": "symfony/cache-contracts",
+ "version": "v2.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/cache-contracts.git",
+ "reference": "ac2e168102a2e06a2624f0379bde94cd5854ced2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/ac2e168102a2e06a2624f0379bde94cd5854ced2",
+ "reference": "ac2e168102a2e06a2624f0379bde94cd5854ced2",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "psr/cache": "^1.0|^2.0|^3.0"
+ },
+ "suggest": {
+ "symfony/cache-implementation": ""
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "2.5-dev"
+ },
+ "thanks": {
+ "name": "symfony/contracts",
+ "url": "https://github.com/symfony/contracts"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Contracts\\Cache\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Generic abstractions related to caching",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/cache-contracts/tree/v2.5.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-08-17T14:20:01+00:00"
+ },
+ {
+ "name": "symfony/deprecation-contracts",
+ "version": "v2.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/deprecation-contracts.git",
+ "reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/6f981ee24cf69ee7ce9736146d1c57c2780598a8",
+ "reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "2.5-dev"
+ },
+ "thanks": {
+ "name": "symfony/contracts",
+ "url": "https://github.com/symfony/contracts"
+ }
+ },
+ "autoload": {
+ "files": [
+ "function.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "A generic function and convention to trigger deprecation notices",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-07-12T14:48:14+00:00"
+ },
+ {
+ "name": "symfony/event-dispatcher",
+ "version": "v5.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/event-dispatcher.git",
+ "reference": "27d39ae126352b9fa3be5e196ccf4617897be3eb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/27d39ae126352b9fa3be5e196ccf4617897be3eb",
+ "reference": "27d39ae126352b9fa3be5e196ccf4617897be3eb",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "symfony/deprecation-contracts": "^2.1|^3",
+ "symfony/event-dispatcher-contracts": "^2|^3",
+ "symfony/polyfill-php80": "^1.16"
+ },
+ "conflict": {
+ "symfony/dependency-injection": "<4.4"
+ },
+ "provide": {
+ "psr/event-dispatcher-implementation": "1.0",
+ "symfony/event-dispatcher-implementation": "2.0"
+ },
+ "require-dev": {
+ "psr/log": "^1|^2|^3",
+ "symfony/config": "^4.4|^5.0|^6.0",
+ "symfony/dependency-injection": "^4.4|^5.0|^6.0",
+ "symfony/error-handler": "^4.4|^5.0|^6.0",
+ "symfony/expression-language": "^4.4|^5.0|^6.0",
+ "symfony/http-foundation": "^4.4|^5.0|^6.0",
+ "symfony/service-contracts": "^1.1|^2|^3",
+ "symfony/stopwatch": "^4.4|^5.0|^6.0"
+ },
+ "suggest": {
+ "symfony/dependency-injection": "",
+ "symfony/http-kernel": ""
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\EventDispatcher\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/event-dispatcher/tree/v5.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-11-23T10:19:22+00:00"
+ },
+ {
+ "name": "symfony/event-dispatcher-contracts",
+ "version": "v2.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/event-dispatcher-contracts.git",
+ "reference": "66bea3b09be61613cd3b4043a65a8ec48cfa6d2a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/66bea3b09be61613cd3b4043a65a8ec48cfa6d2a",
+ "reference": "66bea3b09be61613cd3b4043a65a8ec48cfa6d2a",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "psr/event-dispatcher": "^1"
+ },
+ "suggest": {
+ "symfony/event-dispatcher-implementation": ""
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "2.5-dev"
+ },
+ "thanks": {
+ "name": "symfony/contracts",
+ "url": "https://github.com/symfony/contracts"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Contracts\\EventDispatcher\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Generic abstractions related to dispatching event",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v2.5.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-07-12T14:48:14+00:00"
+ },
+ {
+ "name": "symfony/expression-language",
+ "version": "v5.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/expression-language.git",
+ "reference": "aff6ee3cf4ac1f37f5c7dad3f89f439dbe0893f2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/expression-language/zipball/aff6ee3cf4ac1f37f5c7dad3f89f439dbe0893f2",
+ "reference": "aff6ee3cf4ac1f37f5c7dad3f89f439dbe0893f2",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "symfony/cache": "^4.4|^5.0|^6.0",
+ "symfony/service-contracts": "^1.1|^2|^3"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\ExpressionLanguage\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides an engine that can compile and evaluate expressions",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/expression-language/tree/v5.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-11-23T10:19:22+00:00"
+ },
+ {
+ "name": "symfony/http-foundation",
+ "version": "v5.4.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/http-foundation.git",
+ "reference": "5dad3780023a707f4c24beac7d57aead85c1ce3c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/http-foundation/zipball/5dad3780023a707f4c24beac7d57aead85c1ce3c",
+ "reference": "5dad3780023a707f4c24beac7d57aead85c1ce3c",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "symfony/deprecation-contracts": "^2.1|^3",
+ "symfony/polyfill-mbstring": "~1.1",
+ "symfony/polyfill-php80": "^1.16"
+ },
+ "require-dev": {
+ "predis/predis": "~1.0",
+ "symfony/cache": "^4.4|^5.0|^6.0",
+ "symfony/expression-language": "^4.4|^5.0|^6.0",
+ "symfony/mime": "^4.4|^5.0|^6.0"
+ },
+ "suggest": {
+ "symfony/mime": "To use the file extension guesser"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\HttpFoundation\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Defines an object-oriented layer for the HTTP specification",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/http-foundation/tree/v5.4.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-12-09T12:46:57+00:00"
+ },
+ {
+ "name": "symfony/polyfill-mbstring",
+ "version": "v1.23.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-mbstring.git",
+ "reference": "9174a3d80210dca8daa7f31fec659150bbeabfc6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9174a3d80210dca8daa7f31fec659150bbeabfc6",
+ "reference": "9174a3d80210dca8daa7f31fec659150bbeabfc6",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "suggest": {
+ "ext-mbstring": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.23-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Mbstring\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for the Mbstring extension",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "mbstring",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.23.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-05-27T12:26:48+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php73",
+ "version": "v1.23.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php73.git",
+ "reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fba8933c384d6476ab14fb7b8526e5287ca7e010",
+ "reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.23-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Php73\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ],
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php73/tree/v1.23.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-02-19T12:13:01+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php80",
+ "version": "v1.23.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php80.git",
+ "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/1100343ed1a92e3a38f9ae122fc0eb21602547be",
+ "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.23-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Php80\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ],
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ion Bazan",
+ "email": "ion.bazan@gmail.com"
+ },
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php80/tree/v1.23.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-07-28T13:41:28+00:00"
+ },
+ {
+ "name": "symfony/process",
+ "version": "v4.4.35",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/process.git",
+ "reference": "c2098705326addae6e6742151dfade47ac71da1b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/process/zipball/c2098705326addae6e6742151dfade47ac71da1b",
+ "reference": "c2098705326addae6e6742151dfade47ac71da1b",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.1.3",
+ "symfony/polyfill-php80": "^1.16"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Process\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Executes commands in sub-processes",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/process/tree/v4.4.35"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-11-22T22:36:24+00:00"
+ },
+ {
+ "name": "symfony/psr-http-message-bridge",
+ "version": "v2.1.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/psr-http-message-bridge.git",
+ "reference": "22b37c8a3f6b5d94e9cdbd88e1270d96e2f97b34"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/22b37c8a3f6b5d94e9cdbd88e1270d96e2f97b34",
+ "reference": "22b37c8a3f6b5d94e9cdbd88e1270d96e2f97b34",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.1",
+ "psr/http-message": "^1.0",
+ "symfony/http-foundation": "^4.4 || ^5.0 || ^6.0"
+ },
+ "require-dev": {
+ "nyholm/psr7": "^1.1",
+ "psr/log": "^1.1 || ^2 || ^3",
+ "symfony/browser-kit": "^4.4 || ^5.0 || ^6.0",
+ "symfony/config": "^4.4 || ^5.0 || ^6.0",
+ "symfony/event-dispatcher": "^4.4 || ^5.0 || ^6.0",
+ "symfony/framework-bundle": "^4.4 || ^5.0 || ^6.0",
+ "symfony/http-kernel": "^4.4 || ^5.0 || ^6.0",
+ "symfony/phpunit-bridge": "^5.4@dev || ^6.0"
+ },
+ "suggest": {
+ "nyholm/psr7": "For a super lightweight PSR-7/17 implementation"
+ },
+ "type": "symfony-bridge",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "2.1-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Bridge\\PsrHttpMessage\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "http://symfony.com/contributors"
+ }
+ ],
+ "description": "PSR HTTP message bridge",
+ "homepage": "http://symfony.com",
+ "keywords": [
+ "http",
+ "http-message",
+ "psr-17",
+ "psr-7"
+ ],
+ "support": {
+ "issues": "https://github.com/symfony/psr-http-message-bridge/issues",
+ "source": "https://github.com/symfony/psr-http-message-bridge/tree/v2.1.2"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-11-05T13:13:39+00:00"
+ },
+ {
+ "name": "symfony/service-contracts",
+ "version": "v2.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/service-contracts.git",
+ "reference": "1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/service-contracts/zipball/1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc",
+ "reference": "1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "psr/container": "^1.1",
+ "symfony/deprecation-contracts": "^2.1"
+ },
+ "conflict": {
+ "ext-psr": "<1.1|>=2"
+ },
+ "suggest": {
+ "symfony/service-implementation": ""
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "2.5-dev"
+ },
+ "thanks": {
+ "name": "symfony/contracts",
+ "url": "https://github.com/symfony/contracts"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Contracts\\Service\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Generic abstractions related to writing services",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/service-contracts/tree/v2.5.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-11-04T16:48:04+00:00"
+ },
+ {
+ "name": "symfony/translation",
+ "version": "v5.4.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/translation.git",
+ "reference": "8c82cd35ed861236138d5ae1c78c0c7ebcd62107"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/translation/zipball/8c82cd35ed861236138d5ae1c78c0c7ebcd62107",
+ "reference": "8c82cd35ed861236138d5ae1c78c0c7ebcd62107",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "symfony/deprecation-contracts": "^2.1|^3",
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/polyfill-php80": "^1.16",
+ "symfony/translation-contracts": "^2.3"
+ },
+ "conflict": {
+ "symfony/config": "<4.4",
+ "symfony/console": "<5.3",
+ "symfony/dependency-injection": "<5.0",
+ "symfony/http-kernel": "<5.0",
+ "symfony/twig-bundle": "<5.0",
+ "symfony/yaml": "<4.4"
+ },
+ "provide": {
+ "symfony/translation-implementation": "2.3"
+ },
+ "require-dev": {
+ "psr/log": "^1|^2|^3",
+ "symfony/config": "^4.4|^5.0|^6.0",
+ "symfony/console": "^5.4|^6.0",
+ "symfony/dependency-injection": "^5.0|^6.0",
+ "symfony/finder": "^4.4|^5.0|^6.0",
+ "symfony/http-client-contracts": "^1.1|^2.0|^3.0",
+ "symfony/http-kernel": "^5.0|^6.0",
+ "symfony/intl": "^4.4|^5.0|^6.0",
+ "symfony/polyfill-intl-icu": "^1.21",
+ "symfony/service-contracts": "^1.1.2|^2|^3",
+ "symfony/yaml": "^4.4|^5.0|^6.0"
+ },
+ "suggest": {
+ "psr/log-implementation": "To use logging capability in translator",
+ "symfony/config": "",
+ "symfony/yaml": ""
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "Resources/functions.php"
+ ],
+ "psr-4": {
+ "Symfony\\Component\\Translation\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides tools to internationalize your application",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/translation/tree/v5.4.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-12-05T20:33:52+00:00"
+ },
+ {
+ "name": "symfony/translation-contracts",
+ "version": "v2.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/translation-contracts.git",
+ "reference": "d28150f0f44ce854e942b671fc2620a98aae1b1e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/d28150f0f44ce854e942b671fc2620a98aae1b1e",
+ "reference": "d28150f0f44ce854e942b671fc2620a98aae1b1e",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.2.5"
+ },
+ "suggest": {
+ "symfony/translation-implementation": ""
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "2.5-dev"
+ },
+ "thanks": {
+ "name": "symfony/contracts",
+ "url": "https://github.com/symfony/contracts"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Contracts\\Translation\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Generic abstractions related to translation",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/translation-contracts/tree/v2.5.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-08-17T14:20:01+00:00"
+ },
+ {
+ "name": "symfony/var-exporter",
+ "version": "v5.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/var-exporter.git",
+ "reference": "d59446d6166b1643a8a3c30c2fa8e16e51cdbde7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/var-exporter/zipball/d59446d6166b1643a8a3c30c2fa8e16e51cdbde7",
+ "reference": "d59446d6166b1643a8a3c30c2fa8e16e51cdbde7",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "symfony/polyfill-php80": "^1.16"
+ },
+ "require-dev": {
+ "symfony/var-dumper": "^4.4.9|^5.0.9|^6.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\VarExporter\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Allows exporting any serializable PHP data structure to plain PHP code",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "clone",
+ "construct",
+ "export",
+ "hydrate",
+ "instantiate",
+ "serialize"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/var-exporter/tree/v5.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-11-22T10:44:13+00:00"
+ },
+ {
+ "name": "topthink/framework",
+ "version": "v6.0.9",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/top-think/framework.git",
+ "reference": "0b5fb453f0e533de3af3a1ab6a202510b61be617"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/top-think/framework/zipball/0b5fb453f0e533de3af3a1ab6a202510b61be617",
+ "reference": "0b5fb453f0e533de3af3a1ab6a202510b61be617",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "ext-json": "*",
+ "ext-mbstring": "*",
+ "league/flysystem": "^1.1.4",
+ "league/flysystem-cached-adapter": "^1.0",
+ "php": ">=7.2.5",
+ "psr/container": "~1.0",
+ "psr/log": "~1.0",
+ "psr/simple-cache": "^1.0",
+ "topthink/think-helper": "^3.1.1",
+ "topthink/think-orm": "^2.0"
+ },
+ "require-dev": {
+ "mikey179/vfsstream": "^1.6",
+ "mockery/mockery": "^1.2",
+ "phpunit/phpunit": "^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [],
+ "psr-4": {
+ "think\\": "src/think/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "liu21st",
+ "email": "liu21st@gmail.com"
+ },
+ {
+ "name": "yunwuxin",
+ "email": "448901948@qq.com"
+ }
+ ],
+ "description": "The ThinkPHP Framework.",
+ "homepage": "http://thinkphp.cn/",
+ "keywords": [
+ "framework",
+ "orm",
+ "thinkphp"
+ ],
+ "support": {
+ "issues": "https://github.com/top-think/framework/issues",
+ "source": "https://github.com/top-think/framework/tree/v6.0.9"
+ },
+ "time": "2021-07-22T03:24:49+00:00"
+ },
+ {
+ "name": "topthink/think-captcha",
+ "version": "v3.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/top-think/think-captcha.git",
+ "reference": "1eef3717c1bcf4f5bbe2d1a1c704011d330a8b55"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/top-think/think-captcha/zipball/1eef3717c1bcf4f5bbe2d1a1c704011d330a8b55",
+ "reference": "1eef3717c1bcf4f5bbe2d1a1c704011d330a8b55",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "topthink/framework": "^6.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "think": {
+ "services": [
+ "think\\captcha\\CaptchaService"
+ ],
+ "config": {
+ "captcha": "src/config.php"
+ }
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "think\\captcha\\": "src/"
+ },
+ "files": [
+ "src/helper.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "yunwuxin",
+ "email": "448901948@qq.com"
+ }
+ ],
+ "description": "captcha package for thinkphp",
+ "support": {
+ "issues": "https://github.com/top-think/think-captcha/issues",
+ "source": "https://github.com/top-think/think-captcha/tree/v3.0.3"
+ },
+ "time": "2020-05-19T10:55:45+00:00"
+ },
+ {
+ "name": "topthink/think-helper",
+ "version": "v3.1.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/top-think/think-helper.git",
+ "reference": "f98e3ad44acd27ae85a4d923b1bdfd16c6d8d905"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/top-think/think-helper/zipball/f98e3ad44acd27ae85a4d923b1bdfd16c6d8d905",
+ "reference": "f98e3ad44acd27ae85a4d923b1bdfd16c6d8d905",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.1.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "think\\": "src"
+ },
+ "files": [
+ "src/helper.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "yunwuxin",
+ "email": "448901948@qq.com"
+ }
+ ],
+ "description": "The ThinkPHP6 Helper Package",
+ "support": {
+ "issues": "https://github.com/top-think/think-helper/issues",
+ "source": "https://github.com/top-think/think-helper/tree/v3.1.5"
+ },
+ "time": "2021-06-21T06:17:31+00:00"
+ },
+ {
+ "name": "topthink/think-image",
+ "version": "v1.0.7",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/top-think/think-image.git",
+ "reference": "8586cf47f117481c6d415b20f7dedf62e79d5512"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/top-think/think-image/zipball/8586cf47f117481c6d415b20f7dedf62e79d5512",
+ "reference": "8586cf47f117481c6d415b20f7dedf62e79d5512",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "ext-gd": "*"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "4.8.*",
+ "topthink/framework": "^5.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "think\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "yunwuxin",
+ "email": "448901948@qq.com"
+ }
+ ],
+ "description": "The ThinkPHP5 Image Package",
+ "support": {
+ "issues": "https://github.com/top-think/think-image/issues",
+ "source": "https://github.com/top-think/think-image/tree/master"
+ },
+ "time": "2016-09-29T06:05:43+00:00"
+ },
+ {
+ "name": "topthink/think-migration",
+ "version": "v3.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/top-think/think-migration.git",
+ "reference": "5717d9e5f3ea745f6dbfd1e30b4402aaadff9a79"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/top-think/think-migration/zipball/5717d9e5f3ea745f6dbfd1e30b4402aaadff9a79",
+ "reference": "5717d9e5f3ea745f6dbfd1e30b4402aaadff9a79",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "topthink/framework": "^6.0.0",
+ "topthink/think-helper": "^3.0.3"
+ },
+ "require-dev": {
+ "fzaninotto/faker": "^1.8"
+ },
+ "suggest": {
+ "fzaninotto/faker": "Required to use the factory builder (^1.8)."
+ },
+ "type": "library",
+ "extra": {
+ "think": {
+ "services": [
+ "think\\migration\\Service"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Phinx\\": "phinx/src/Phinx",
+ "think\\migration\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "yunwuxin",
+ "email": "448901948@qq.com"
+ }
+ ],
+ "support": {
+ "issues": "https://github.com/top-think/think-migration/issues",
+ "source": "https://github.com/top-think/think-migration/tree/v3.0.3"
+ },
+ "time": "2020-12-07T05:54:22+00:00"
+ },
+ {
+ "name": "topthink/think-orm",
+ "version": "v2.0.45",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/top-think/think-orm.git",
+ "reference": "3dcf9af447b048103093840833e8c74ab849152f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/top-think/think-orm/zipball/3dcf9af447b048103093840833e8c74ab849152f",
+ "reference": "3dcf9af447b048103093840833e8c74ab849152f",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "ext-json": "*",
+ "ext-pdo": "*",
+ "php": ">=7.1.0",
+ "psr/log": "~1.0",
+ "psr/simple-cache": "^1.0",
+ "topthink/think-helper": "^3.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^7|^8|^9.5"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "think\\": "src"
+ },
+ "files": [
+ "stubs/load_stubs.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "liu21st",
+ "email": "liu21st@gmail.com"
+ }
+ ],
+ "description": "think orm",
+ "keywords": [
+ "database",
+ "orm"
+ ],
+ "support": {
+ "issues": "https://github.com/top-think/think-orm/issues",
+ "source": "https://github.com/top-think/think-orm/tree/v2.0.45"
+ },
+ "time": "2021-11-30T14:31:05+00:00"
+ },
+ {
+ "name": "topthink/think-queue",
+ "version": "v3.0.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/top-think/think-queue.git",
+ "reference": "a9f81126bdd52d036461e0c6556592dd478c8728"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/top-think/think-queue/zipball/a9f81126bdd52d036461e0c6556592dd478c8728",
+ "reference": "a9f81126bdd52d036461e0c6556592dd478c8728",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "ext-json": "*",
+ "nesbot/carbon": "^2.16",
+ "symfony/process": "^4.2",
+ "topthink/framework": "^6.0"
+ },
+ "require-dev": {
+ "mockery/mockery": "^1.2",
+ "phpunit/phpunit": "^6.2",
+ "topthink/think-migration": "^3.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "think": {
+ "services": [
+ "think\\queue\\Service"
+ ],
+ "config": {
+ "queue": "src/config.php"
+ }
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "think\\": "src"
+ },
+ "files": [
+ "src/common.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "yunwuxin",
+ "email": "448901948@qq.com"
+ }
+ ],
+ "description": "The ThinkPHP6 Queue Package",
+ "support": {
+ "issues": "https://github.com/top-think/think-queue/issues",
+ "source": "https://github.com/top-think/think-queue/tree/v3.0.6"
+ },
+ "time": "2021-06-24T18:03:45+00:00"
+ },
+ {
+ "name": "topthink/think-template",
+ "version": "v2.0.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/top-think/think-template.git",
+ "reference": "abfc293f74f9ef5127b5c416310a01fe42e59368"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/top-think/think-template/zipball/abfc293f74f9ef5127b5c416310a01fe42e59368",
+ "reference": "abfc293f74f9ef5127b5c416310a01fe42e59368",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.1.0",
+ "psr/simple-cache": "^1.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "think\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "liu21st",
+ "email": "liu21st@gmail.com"
+ }
+ ],
+ "description": "the php template engine",
+ "support": {
+ "issues": "https://github.com/top-think/think-template/issues",
+ "source": "https://github.com/top-think/think-template/tree/v2.0.8"
+ },
+ "time": "2020-12-10T07:52:03+00:00"
+ },
+ {
+ "name": "topthink/think-view",
+ "version": "v1.0.14",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/top-think/think-view.git",
+ "reference": "edce0ae2c9551ab65f9e94a222604b0dead3576d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/top-think/think-view/zipball/edce0ae2c9551ab65f9e94a222604b0dead3576d",
+ "reference": "edce0ae2c9551ab65f9e94a222604b0dead3576d",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.1.0",
+ "topthink/think-template": "^2.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "think\\view\\driver\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "liu21st",
+ "email": "liu21st@gmail.com"
+ }
+ ],
+ "description": "thinkphp template driver",
+ "support": {
+ "issues": "https://github.com/top-think/think-view/issues",
+ "source": "https://github.com/top-think/think-view/tree/v1.0.14"
+ },
+ "time": "2019-11-06T11:40:13+00:00"
+ },
+ {
+ "name": "yansongda/pay",
+ "version": "v2.10.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/yansongda/pay.git",
+ "reference": "8c258853b142c6d7589629b047ca5cb21232b509"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/yansongda/pay/zipball/8c258853b142c6d7589629b047ca5cb21232b509",
+ "reference": "8c258853b142c6d7589629b047ca5cb21232b509",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "ext-bcmath": "*",
+ "ext-json": "*",
+ "ext-libxml": "*",
+ "ext-openssl": "*",
+ "ext-simplexml": "*",
+ "php": ">=7.1.3",
+ "symfony/event-dispatcher": "^4.0 || ^5.0",
+ "symfony/http-foundation": "^4.0 || ^5.0.7",
+ "yansongda/supports": "^2.0"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "^2.15",
+ "mockery/mockery": "^1.2",
+ "phpunit/phpunit": "^7.5"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Yansongda\\Pay\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "yansongda",
+ "email": "me@yansongda.cn"
+ }
+ ],
+ "description": "专注 Alipay 和 WeChat 的支付扩展包",
+ "keywords": [
+ "alipay",
+ "pay",
+ "wechat"
+ ],
+ "support": {
+ "issues": "https://github.com/yansongda/pay/issues",
+ "source": "https://github.com/yansongda/pay"
+ },
+ "time": "2021-01-18T01:48:43+00:00"
+ },
+ {
+ "name": "yansongda/supports",
+ "version": "v2.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/yansongda/supports.git",
+ "reference": "de9a8d38b0461ddf9c12f27390dad9a40c9b4e3b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/yansongda/supports/zipball/de9a8d38b0461ddf9c12f27390dad9a40c9b4e3b",
+ "reference": "de9a8d38b0461ddf9c12f27390dad9a40c9b4e3b",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "guzzlehttp/guzzle": "^6.2 || ^7.0",
+ "monolog/monolog": "^1.23 || ^2.0",
+ "php": ">=7.1.3"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "^2.15",
+ "phpunit/phpunit": "^7.5",
+ "predis/predis": "^1.1"
+ },
+ "suggest": {
+ "predis/predis": "Allows to use throttle feature"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Yansongda\\Supports\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "yansongda",
+ "email": "me@yansongda.cn"
+ }
+ ],
+ "description": "common components",
+ "keywords": [
+ "Guzzle",
+ "array",
+ "collection",
+ "config",
+ "http",
+ "support",
+ "throttle"
+ ],
+ "support": {
+ "issues": "https://github.com/yansongda/supports/issues",
+ "source": "https://github.com/yansongda/supports"
+ },
+ "time": "2020-10-14T08:17:18+00:00"
+ }
+ ],
+ "packages-dev": [
+ {
+ "name": "symfony/polyfill-php72",
+ "version": "v1.23.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php72.git",
+ "reference": "9a142215a36a3888e30d0a9eeea9766764e96976"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/9a142215a36a3888e30d0a9eeea9766764e96976",
+ "reference": "9a142215a36a3888e30d0a9eeea9766764e96976",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.23-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Php72\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php72/tree/v1.23.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-05-27T09:17:38+00:00"
+ },
+ {
+ "name": "symfony/var-dumper",
+ "version": "v4.4.34",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/var-dumper.git",
+ "reference": "2d0c056b2faaa3d785bdbd5adecc593a5be9c16e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/var-dumper/zipball/2d0c056b2faaa3d785bdbd5adecc593a5be9c16e",
+ "reference": "2d0c056b2faaa3d785bdbd5adecc593a5be9c16e",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.1.3",
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/polyfill-php72": "~1.5",
+ "symfony/polyfill-php80": "^1.16"
+ },
+ "conflict": {
+ "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0",
+ "symfony/console": "<3.4"
+ },
+ "require-dev": {
+ "ext-iconv": "*",
+ "symfony/console": "^3.4|^4.0|^5.0",
+ "symfony/process": "^4.4|^5.0",
+ "twig/twig": "^1.43|^2.13|^3.0.4"
+ },
+ "suggest": {
+ "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).",
+ "ext-intl": "To show region name in time zone dump",
+ "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script"
+ },
+ "bin": [
+ "Resources/bin/var-dump-server"
+ ],
+ "type": "library",
+ "autoload": {
+ "files": [
+ "Resources/functions/dump.php"
+ ],
+ "psr-4": {
+ "Symfony\\Component\\VarDumper\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides mechanisms for walking through any arbitrary PHP variable",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "debug",
+ "dump"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/var-dumper/tree/v4.4.34"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-11-12T10:50:54+00:00"
+ },
+ {
+ "name": "topthink/think-trace",
+ "version": "v1.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/top-think/think-trace.git",
+ "reference": "9a9fa8f767b6c66c5a133ad21ca1bc96ad329444"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/top-think/think-trace/zipball/9a9fa8f767b6c66c5a133ad21ca1bc96ad329444",
+ "reference": "9a9fa8f767b6c66c5a133ad21ca1bc96ad329444",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.1.0",
+ "topthink/framework": "^6.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "think": {
+ "services": [
+ "think\\trace\\Service"
+ ],
+ "config": {
+ "trace": "src/config.php"
+ }
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "think\\trace\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "liu21st",
+ "email": "liu21st@gmail.com"
+ }
+ ],
+ "description": "thinkphp debug trace",
+ "support": {
+ "issues": "https://github.com/top-think/think-trace/issues",
+ "source": "https://github.com/top-think/think-trace/tree/v1.4"
+ },
+ "time": "2020-06-29T05:27:28+00:00"
+ }
+ ],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": {
+ "php": ">=7.4.0",
+ "ext-json": "*",
+ "ext-curl": "*",
+ "ext-bcmath": "*"
+ },
+ "platform-dev": [],
+ "plugin-api-version": "2.2.0"
+}
diff --git a/config/app.php b/config/app.php
new file mode 100644
index 0000000..2bdef3b
--- /dev/null
+++ b/config/app.php
@@ -0,0 +1,40 @@
+ Env::get('app.host', ''),
+ // 应用的命名空间
+ 'app_namespace' => '',
+ // 是否启用路由
+ 'with_route' => true,
+ // 是否启用事件
+ 'with_event' => true,
+ // 自动多应用模式
+ 'auto_multi_app' => true,
+ // 应用映射(自动多应用模式有效)
+ 'app_map' => [],
+ // 域名绑定(自动多应用模式有效)
+ 'domain_bind' => [],
+ // 禁止URL访问的应用列表(自动多应用模式有效)
+ 'deny_app_list' => [],
+ // 默认应用
+ 'default_app' => 'index',
+ // 默认时区
+ 'default_timezone' => 'Asia/Shanghai',
+
+ // 异常页面的模板文件
+ 'exception_tmpl' => app()->getThinkPath() . 'tpl/think_exception.tpl',
+
+ /*
+ 'http_exception_template' => [
+ 400 => '',
+ 500 => app()->getThinkPath() . 'tpl/think_exception.tpl',
+ ],
+ */
+
+ // 错误显示信息,非调试模式有效
+ 'error_message' => '页面错误!请稍后再试~',
+ // 显示错误信息
+ 'show_error_msg' => true,
+];
diff --git a/config/cache.php b/config/cache.php
new file mode 100644
index 0000000..d1c9715
--- /dev/null
+++ b/config/cache.php
@@ -0,0 +1,37 @@
+ Env::get('cache.driver', 'file'),
+
+ // 缓存连接方式配置
+ 'stores' => [
+ 'file' => [
+ // 驱动方式
+ 'type' => 'File',
+ // 缓存保存目录
+ 'path' => '',
+ // 缓存前缀
+ 'prefix' => '',
+ // 缓存有效期 0表示永久缓存
+ 'expire' => 0,
+ // 缓存标签前缀
+ 'tag_prefix' => 'tag:',
+ // 序列化机制 例如 ['serialize', 'unserialize']
+ 'serialize' => [],
+ ],
+ // redis缓存
+ 'redis' => [
+ // 驱动方式
+ 'type' => 'redis',
+ // 服务器地址
+ 'host' => env('redis.host') ?? '127.0.0.1',
+ ],
+ // 更多的缓存连接
+ ],
+];
diff --git a/config/captcha.php b/config/captcha.php
new file mode 100644
index 0000000..3870042
--- /dev/null
+++ b/config/captcha.php
@@ -0,0 +1,39 @@
+ 4,
+ // 验证码字符集合
+ 'codeSet' => '2345678abcdefhijkmnpqrstuvwxyzABCDEFGHJKLMNPQRTUVWXY',
+ // 验证码过期时间
+ 'expire' => 1800,
+ // 是否使用中文验证码
+ 'useZh' => false,
+ // 是否使用算术验证码
+ 'math' => false,
+ // 是否使用背景图
+ 'useImgBg' => false,
+ //验证码字符大小
+ 'fontSize' => 30,
+ // 是否使用混淆曲线
+ 'useCurve' => false,
+ //是否添加杂点
+ 'useNoise' => true,
+ // 验证码字体 不设置则随机
+ 'fontttf' => '',
+ //背景颜色
+ 'bg' => [243, 251, 254],
+ // 验证码图片高度
+ 'imageH' => 0,
+ // 验证码图片宽度
+ 'imageW' => 0,
+
+ // 添加额外的验证码设置
+ // verify => [
+ // 'length'=>4,
+ // ...
+ //],
+];
diff --git a/config/console.php b/config/console.php
new file mode 100644
index 0000000..a818a98
--- /dev/null
+++ b/config/console.php
@@ -0,0 +1,9 @@
+ [
+ ],
+];
diff --git a/config/cookie.php b/config/cookie.php
new file mode 100644
index 0000000..f728024
--- /dev/null
+++ b/config/cookie.php
@@ -0,0 +1,18 @@
+ 0,
+ // cookie 保存路径
+ 'path' => '/',
+ // cookie 有效域名
+ 'domain' => '',
+ // cookie 启用安全传输
+ 'secure' => false,
+ // httponly设置
+ 'httponly' => false,
+ // 是否使用 setcookie
+ 'setcookie' => true,
+];
diff --git a/config/database.php b/config/database.php
new file mode 100644
index 0000000..abcadc5
--- /dev/null
+++ b/config/database.php
@@ -0,0 +1,63 @@
+ Env::get('database.driver', 'mysql'),
+
+ // 自定义时间查询规则
+ 'time_query_rule' => [],
+
+ // 自动写入时间戳字段
+ // true为自动识别类型 false关闭
+ // 字符串则明确指定时间字段类型 支持 int timestamp datetime date
+ 'auto_timestamp' => true,
+
+ // 时间字段取出后的默认时间格式
+ 'datetime_format' => 'Y-m-d H:i:s',
+
+ // 数据库连接配置信息
+ 'connections' => [
+ 'mysql' => [
+ // 数据库类型
+ 'type' => Env::get('database.type', 'mysql'),
+ // 服务器地址
+ 'hostname' => Env::get('database.hostname', '183.221.101.89'),
+ // 数据库名
+ 'database' => Env::get('database.database', 'newest_cms'),
+ // 用户名
+ 'username' => Env::get('database.username', 'newest_cms'),
+ // 密码
+ 'password' => Env::get('database.password', '7pMZSGFP3fGm526w'),
+ // 端口
+ 'hostport' => Env::get('database.hostport', '3306'),
+ // 数据库连接参数
+ 'params' => [],
+ // 数据库编码默认采用utf8
+ 'charset' => Env::get('database.charset', 'utf8'),
+ // 数据库表前缀
+ 'prefix' => Env::get('database.prefix', 'bee_'),
+
+ // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
+ 'deploy' => 0,
+ // 数据库读写是否分离 主从式有效
+ 'rw_separate' => false,
+ // 读写分离后 主服务器数量
+ 'master_num' => 1,
+ // 指定从服务器序号
+ 'slave_no' => '',
+ // 是否严格检查字段是否存在
+ 'fields_strict' => true,
+ // 是否需要断线重连
+ 'break_reconnect' => false,
+ // 监听SQL
+ 'trigger_sql' => Env::get('database.sql_debug', false),
+ // 开启字段缓存
+ 'fields_cache' => false,
+ // 字段缓存路径
+ 'schema_cache_path' => app()->getRuntimePath() . 'schema' . DIRECTORY_SEPARATOR,
+ ],
+
+ // 更多的数据库配置信息
+ ],
+];
diff --git a/config/filesystem.php b/config/filesystem.php
new file mode 100644
index 0000000..179b93d
--- /dev/null
+++ b/config/filesystem.php
@@ -0,0 +1,52 @@
+ Env::get('filesystem.driver', 'local'),
+ // 磁盘列表
+ 'disks' => [
+ 'local' => [
+ // 磁盘类型
+ 'type' => 'local',
+ // 磁盘路径
+ 'root' => app()->getRootPath() . 'public/storage',
+ // 磁盘路径对应的外部URL路径
+ 'url' => '/storage',
+ // 可见性
+ 'visibility' => 'public',
+ ],
+ 'video' => [
+ // 磁盘类型
+ 'type' => 'local',
+ // 磁盘路径
+ 'root' => app()->getRootPath() . 'public/storage/videos',
+ // 磁盘路径对应的外部URL路径
+ 'url' => '/storage/videos',
+ // 可见性
+ 'visibility' => 'public',
+ ],
+ 'backup' => [
+ // 磁盘类型
+ 'type' => 'local',
+ // 磁盘路径
+ 'root' => app()->getRootPath() . 'public/storage/backup',
+ // 磁盘路径对应的外部URL路径
+ 'url' => '/storage/backup',
+ // 可见性
+ 'visibility' => 'public',
+ ],
+ 'oss' => [
+ // 磁盘类型
+ 'type' => 'local',
+ // 磁盘路径
+ 'root' => app()->getRootPath() . 'public/storage',
+ // 磁盘路径对应的外部URL路径
+ 'url' => '/storage',
+ // 可见性
+ 'visibility' => 'public',
+ ],
+ // 更多的磁盘配置信息
+ ],
+];
diff --git a/config/lang.php b/config/lang.php
new file mode 100644
index 0000000..0db66f9
--- /dev/null
+++ b/config/lang.php
@@ -0,0 +1,27 @@
+ Env::get('lang.default_lang', 'zh-cn'),
+ // 允许的语言列表
+ 'allow_lang_list' => ['zh-cn'],
+ // 多语言自动侦测变量名
+ 'detect_var' => 'lang',
+ // 是否使用Cookie记录
+ 'use_cookie' => true,
+ // 多语言cookie变量
+ 'cookie_var' => 'think_lang',
+ // 扩展语言包
+ 'extend_list' => [],
+ // Accept-Language转义为对应语言包名称
+ 'accept_language' => [
+ 'zh-hans-cn' => 'zh-cn',
+ ],
+ // 是否支持语言分组
+ 'allow_group' => false,
+];
diff --git a/config/log.php b/config/log.php
new file mode 100644
index 0000000..8f00f56
--- /dev/null
+++ b/config/log.php
@@ -0,0 +1,68 @@
+ Env::get('log.channel', 'file'),
+ // 日志记录级别
+ 'level' => [],
+ // 日志类型记录的通道 ['error'=>'email',...]
+ 'type_channel' => [],
+ // 关闭全局日志写入
+ 'close' => false,
+ // 全局日志处理 支持闭包
+ 'processor' => null,
+
+ // 日志通道列表
+ 'channels' => [
+ 'file' => [
+ // 日志记录方式
+ 'type' => 'File',
+ // 日志保存目录
+ 'path' => '',
+ // 单文件日志写入
+ 'single' => false,
+ // 独立日志级别
+ 'apart_level' => [],
+ // 最大日志文件数量
+ 'max_files' => 0,
+ // 使用JSON格式记录
+ 'json' => false,
+ // 日志处理
+ 'processor' => null,
+ // 关闭通道日志写入
+ 'close' => false,
+ // 日志输出格式化
+ 'format' => '[%s][%s] %s',
+ // 是否实时写入
+ 'realtime_write' => false,
+ ],
+ // 其它日志通道配置
+ 'order' => [
+ // 日志记录方式
+ 'type' => 'File',
+ // 日志保存目录
+ 'path' => root_path(). 'order_log',
+ // 单文件日志写入
+ 'single' => false,
+ // 独立日志级别
+ 'apart_level' => [],
+ // 最大日志文件数量
+ 'max_files' => 0,
+ // 使用JSON格式记录
+ 'json' => false,
+ // 日志处理
+ 'processor' => null,
+ // 关闭通道日志写入
+ 'close' => false,
+ // 日志输出格式化
+ 'format' => '[%s][%s] %s',
+ // 是否实时写入
+ 'realtime_write' => false,
+ ],
+ ],
+
+];
diff --git a/config/middleware.php b/config/middleware.php
new file mode 100644
index 0000000..02398c9
--- /dev/null
+++ b/config/middleware.php
@@ -0,0 +1,15 @@
+ [
+ 'auth' => app\middleware\Auth::class,
+ 'csrf' => app\middleware\Csrf::class,
+ 'jwt' => app\middleware\JWT::class,
+ 'login' => app\middleware\Login::class,
+ 'apiLogin' => app\middleware\ApiLogin::class,
+ 'log' => app\middleware\Log::class,
+ ],
+ // 优先级设置,此数组中的中间件会按照数组中的顺序优先执行
+ 'priority' => [],
+];
diff --git a/config/queue.php b/config/queue.php
new file mode 100644
index 0000000..d6639e5
--- /dev/null
+++ b/config/queue.php
@@ -0,0 +1,41 @@
+
+// +----------------------------------------------------------------------
+
+use think\facade\Env;
+
+return [
+ 'default' => 'redis',
+ 'connections' => [
+ 'sync' => [
+ 'type' => 'sync',
+ ],
+ 'database' => [
+ 'type' => 'database',
+ 'queue' => 'default',
+ 'table' => 'jobs',
+ 'connection' => null,
+ ],
+ 'redis' => [
+ 'type' => 'redis',
+ 'queue' => 'default',
+ 'host' => Env::get('redis.host', '127.0.0.1'),
+ 'port' => Env::get('redis.port', 6379),
+ 'password' => Env::get('redis.password', ''),
+ 'select' => 0,
+ 'timeout' => 0,
+ 'persistent' => false,
+ ],
+ ],
+ 'failed' => [
+ 'type' => 'none',
+ 'table' => 'failed_jobs',
+ ],
+];
diff --git a/config/route.php b/config/route.php
new file mode 100644
index 0000000..288c8f1
--- /dev/null
+++ b/config/route.php
@@ -0,0 +1,51 @@
+ '/',
+ // URL伪静态后缀
+ 'url_html_suffix' => 'html',
+ // URL普通方式参数 用于自动生成
+ 'url_common_param' => true,
+ // 是否开启路由延迟解析
+ 'url_lazy_route' => false,
+ // 是否强制使用路由
+ 'url_route_must' => false,
+ // 合并路由规则
+ 'route_rule_merge' => false,
+ // 路由是否完全匹配
+ 'route_complete_match' => false,
+ // 是否开启路由缓存
+ 'route_check_cache' => false,
+ // 路由缓存连接参数
+ 'route_cache_option' => [],
+ // 路由缓存Key
+ 'route_check_cache_key' => '',
+ // 访问控制器层名称
+ 'controller_layer' => 'controller',
+ // 空控制器名
+ 'empty_controller' => 'Error',
+ // 是否使用控制器后缀
+ 'controller_suffix' => false,
+ // 默认的路由变量规则
+ 'default_route_pattern' => '[\w\.]+',
+ // 是否开启请求缓存 true自动缓存 支持设置请求缓存规则
+ 'request_cache' => false,
+ // 请求缓存有效期
+ 'request_cache_expire' => null,
+ // 全局请求缓存排除规则
+ 'request_cache_except' => [],
+ // 默认控制器名
+ 'default_controller' => 'Index',
+ // 默认操作名
+ 'default_action' => 'index',
+ // 操作方法后缀
+ 'action_suffix' => '',
+ // 默认JSONP格式返回的处理方法
+ 'default_jsonp_handler' => 'jsonpReturn',
+ // 默认JSONP处理方法
+ 'var_jsonp_handler' => 'callback',
+];
diff --git a/config/session.php b/config/session.php
new file mode 100644
index 0000000..dfdc544
--- /dev/null
+++ b/config/session.php
@@ -0,0 +1,21 @@
+ 'PHPSESSID',
+ // SESSION_ID的提交变量,解决flash上传跨域
+ 'var_session_id' => '',
+ // 驱动方式 支持file cache
+ 'type' => 'file',
+ // 存储连接标识 当type使用cache的时候有效
+ 'store' => null,
+ // 过期时间(秒)
+ 'expire' => Env::get('app.expire', 3600 * 2),
+ // 前缀
+ 'prefix' => '',
+];
diff --git a/config/tauthz-rbac-model.conf b/config/tauthz-rbac-model.conf
new file mode 100644
index 0000000..7320e08
--- /dev/null
+++ b/config/tauthz-rbac-model.conf
@@ -0,0 +1,14 @@
+[request_definition]
+r = sub, obj, act
+
+[policy_definition]
+p = sub, obj, act
+
+[role_definition]
+g = _, _
+
+[policy_effect]
+e = some(where (p.eft == allow))
+
+[matchers]
+m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
\ No newline at end of file
diff --git a/config/tauthz.php b/config/tauthz.php
new file mode 100644
index 0000000..3ec750c
--- /dev/null
+++ b/config/tauthz.php
@@ -0,0 +1,70 @@
+ 'basic',
+
+ 'log' => [
+ // changes whether Lauthz will log messages to the Logger.
+ 'enabled' => false,
+ // Casbin Logger, Supported: \Psr\Log\LoggerInterface|string
+ 'logger' => 'log',
+ ],
+
+ 'enforcers' => [
+ 'basic' => [
+ /*
+ * Model 设置
+ */
+ 'model' => [
+ // 可选值: "file", "text"
+ 'config_type' => 'file',
+ 'config_file_path' => config_path().'tauthz-rbac-model.conf',
+ 'config_text' => '',
+ ],
+
+ // 适配器 .
+ 'adapter' => tauthz\adapter\DatabaseAdapter::class,
+
+ /*
+ * 数据库设置.
+ */
+ 'database' => [
+ // 数据库连接名称,不填为默认配置.
+ 'connection' => '',
+ // 策略表名(不含表前缀)
+ 'rules_name' => 'rules',
+ // 策略表完整名称.
+ 'rules_table' => null,
+ ],
+ ],
+ 'frontend' => [
+ /*
+ * Model 设置
+ */
+ 'model' => [
+ // 可选值: "file", "text"
+ 'config_type' => 'file',
+ 'config_file_path' => config_path().'tauthz-rbac-model.conf',
+ 'config_text' => '',
+ ],
+
+ // 适配器 .
+ 'adapter' => tauthz\adapter\DatabaseAdapter::class,
+
+ /*
+ * 数据库设置.
+ */
+ 'database' => [
+ // 数据库连接名称,不填为默认配置.
+ 'connection' => '',
+ // 策略表名(不含表前缀)
+ 'rules_name' => 'account_rules',
+ // 策略表完整名称.
+ 'rules_table' => null,
+ ],
+ ],
+ ],
+];
diff --git a/config/trace.php b/config/trace.php
new file mode 100644
index 0000000..fad2392
--- /dev/null
+++ b/config/trace.php
@@ -0,0 +1,10 @@
+ 'Html',
+ // 读取的日志通道名
+ 'channel' => '',
+];
diff --git a/config/view.php b/config/view.php
new file mode 100644
index 0000000..f1da73b
--- /dev/null
+++ b/config/view.php
@@ -0,0 +1,34 @@
+ 'Think',
+ // 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写 3 保持操作方法
+ 'auto_rule' => 1,
+ // 模板目录名
+ 'view_dir_name' => 'view',
+ // 模板后缀
+ 'view_suffix' => 'html',
+ // 模板文件名分隔符
+ 'view_depr' => DIRECTORY_SEPARATOR,
+ // 模板引擎普通标签开始标记
+ 'tpl_begin' => '{',
+ // 模板引擎普通标签结束标记
+ 'tpl_end' => '}',
+ // 标签库标签开始标记
+ 'taglib_begin' => '{',
+ // 标签库标签结束标记
+ 'taglib_end' => '}',
+ 'tpl_replace_string' => [
+ '__STATIC__' => '/static',
+ '__MANAGER__' => '/static/manager',
+ '__LAYUIMINI__' => '/static/layuimini',
+ '__COMMON__' => '/static/common',
+ '__JS__' => '/static/js',
+ '__CSS__' => '/static/css',
+ '__IMG__' => '/static/images',
+ ],
+];
diff --git a/favicon.ico b/favicon.ico
new file mode 100644
index 0000000..20232b9
Binary files /dev/null and b/favicon.ico differ
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..978c4ed
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,5 @@
+{
+ "name": "cms",
+ "version": "1.0.0",
+ "lockfileVersion": 1
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..98a6274
--- /dev/null
+++ b/package.json
@@ -0,0 +1,16 @@
+{
+ "name": "cms",
+ "version": "1.0.0",
+ "description": "本CMS基于ThinkPHP 6.0开发",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://gitee.com/dxtc/cms.git"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC"
+}
diff --git a/public/.htaccess b/public/.htaccess
new file mode 100644
index 0000000..8aa9d23
--- /dev/null
+++ b/public/.htaccess
@@ -0,0 +1,9 @@
+
+ Options +FollowSymlinks -Multiviews
+ RewriteEngine On
+
+ RewriteCond %{REQUEST_FILENAME} !-d
+ RewriteCond %{REQUEST_FILENAME} !-f
+ RewriteCond %{REQUEST_URI} !^(.*)\.(gif|jpg|jpeg|png|swf|mp4)$ [NC]
+ RewriteRule ^(.*)$ index.php?s=/$1 [QSA,PT,L]
+
\ No newline at end of file
diff --git a/public/favicon.ico b/public/favicon.ico
new file mode 100644
index 0000000..20232b9
Binary files /dev/null and b/public/favicon.ico differ
diff --git a/public/index.php b/public/index.php
new file mode 100644
index 0000000..bbbd56a
--- /dev/null
+++ b/public/index.php
@@ -0,0 +1,21 @@
+
+// +----------------------------------------------------------------------
+
+// [ 应用入口文件 ]
+namespace think;
+
+require dirname(__DIR__) . '/vendor/autoload.php';
+
+// 执行HTTP应用并响应
+$http = (new App())->http;
+$response = $http->run();
+$response->send();
+$http->end($response);
\ No newline at end of file
diff --git a/public/static/api/clear.json b/public/static/api/clear.json
new file mode 100644
index 0000000..e0f5ed7
--- /dev/null
+++ b/public/static/api/clear.json
@@ -0,0 +1,4 @@
+{
+ "code": 1,
+ "msg": "服务端清理缓存成功"
+}
\ No newline at end of file
diff --git a/public/static/api/init.json b/public/static/api/init.json
new file mode 100644
index 0000000..73a459d
--- /dev/null
+++ b/public/static/api/init.json
@@ -0,0 +1,221 @@
+{
+ "homeInfo": {
+ "title": "控制台",
+ "href": "manager/index/dashboard"
+ },
+ "logoInfo": {
+ "title": "LAYUI MINI",
+ "image": "/static/layuimini/images/logo.png",
+ "href": ""
+ },
+ "menuInfo": [
+ {
+ "title": "常规管理",
+ "icon": "fa fa-address-book",
+ "href": "",
+ "target": "_self",
+ "child": [
+ {
+ "title": "区域",
+ "href": "manager/index/area.html",
+ "icon": "fa fa-home",
+ "target": "_self"
+ },
+ {
+ "title": "菜单管理",
+ "href": "manager/menu",
+ "icon": "fa fa-window-maximize",
+ "target": "_self"
+ },
+ {
+ "title": "表单",
+ "href": "manager/index/form.html",
+ "icon": "fa fa-gears",
+ "target": "_self"
+ },
+ {
+ "title": "菜单2",
+ "href": "manager/menu/table.html",
+ "icon": "fa fa-gears",
+ "target": "_self"
+ },
+ {
+ "title": "表单示例",
+ "href": "",
+ "icon": "fa fa-calendar",
+ "target": "_self",
+ "child": [
+ {
+ "title": "普通表单",
+ "href": "page/form.html",
+ "icon": "fa fa-list-alt",
+ "target": "_self"
+ },
+ {
+ "title": "分步表单",
+ "href": "page/form-step.html",
+ "icon": "fa fa-navicon",
+ "target": "_self"
+ }
+ ]
+ },
+ {
+ "title": "异常页面",
+ "href": "",
+ "icon": "fa fa-home",
+ "target": "_self",
+ "child": [
+ {
+ "title": "404页面",
+ "href": "page/404.html",
+ "icon": "fa fa-hourglass-end",
+ "target": "_self"
+ }
+ ]
+ },
+ {
+ "title": "其它界面",
+ "href": "",
+ "icon": "fa fa-snowflake-o",
+ "target": "",
+ "child": [
+ {
+ "title": "按钮示例",
+ "href": "page/button.html",
+ "icon": "fa fa-snowflake-o",
+ "target": "_self"
+ },
+ {
+ "title": "弹出层",
+ "href": "page/layer.html",
+ "icon": "fa fa-shield",
+ "target": "_self"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "title": "组件管理",
+ "icon": "fa fa-lemon-o",
+ "href": "",
+ "target": "_self",
+ "child": [
+ {
+ "title": "图标列表",
+ "href": "page/icon.html",
+ "icon": "fa fa-dot-circle-o",
+ "target": "_self"
+ },
+ {
+ "title": "图标选择",
+ "href": "page/icon-picker.html",
+ "icon": "fa fa-adn",
+ "target": "_self"
+ },
+ {
+ "title": "颜色选择",
+ "href": "page/color-select.html",
+ "icon": "fa fa-dashboard",
+ "target": "_self"
+ },
+ {
+ "title": "下拉选择",
+ "href": "page/table-select.html",
+ "icon": "fa fa-angle-double-down",
+ "target": "_self"
+ },
+ {
+ "title": "文件上传",
+ "href": "page/upload.html",
+ "icon": "fa fa-arrow-up",
+ "target": "_self"
+ },
+ {
+ "title": "富文本编辑器",
+ "href": "page/editor.html",
+ "icon": "fa fa-edit",
+ "target": "_self"
+ },
+ {
+ "title": "省市县区选择器",
+ "href": "page/area.html",
+ "icon": "fa fa-rocket",
+ "target": "_self"
+ }
+ ]
+ },
+ {
+ "title": "其它管理",
+ "icon": "fa fa-slideshare",
+ "href": "",
+ "target": "_self",
+ "child": [
+ {
+ "title": "多级菜单",
+ "href": "",
+ "icon": "fa fa-meetup",
+ "target": "",
+ "child": [
+ {
+ "title": "按钮1",
+ "href": "page/button.html?v=1",
+ "icon": "fa fa-calendar",
+ "target": "_self",
+ "child": [
+ {
+ "title": "按钮2",
+ "href": "page/button.html?v=2",
+ "icon": "fa fa-snowflake-o",
+ "target": "_self",
+ "child": [
+ {
+ "title": "按钮3",
+ "href": "page/button.html?v=3",
+ "icon": "fa fa-snowflake-o",
+ "target": "_self"
+ },
+ {
+ "title": "表单4",
+ "href": "page/form.html?v=1",
+ "icon": "fa fa-calendar",
+ "target": "_self",
+ "child": [
+ {
+ "title": "按钮2",
+ "href": "page/button.html?v=2",
+ "icon": "fa fa-snowflake-o",
+ "target": "_self",
+ "child": [
+ {
+ "title": "按钮3",
+ "href": "page/button.html?v=3",
+ "icon": "fa fa-snowflake-o",
+ "target": "_self"
+ },
+ {
+ "title": "表单4",
+ "href": "page/form.html?v=1",
+ "icon": "fa fa-calendar",
+ "target": "_self"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "title": "失效菜单",
+ "href": "page/error.html",
+ "icon": "fa fa-superpowers",
+ "target": "_self"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/public/static/api/menu2.json b/public/static/api/menu2.json
new file mode 100644
index 0000000..23d036a
--- /dev/null
+++ b/public/static/api/menu2.json
@@ -0,0 +1,102 @@
+{
+ "code": 0,
+ "msg": "",
+ "count": 19,
+ "data": [
+ {
+ "id": 1,
+ "title": "系统管理",
+ "sort": 1,
+ "menuUrl": null,
+ "icon": "layui-icon-set",
+ "created_at": "2018/06/29 11:05:41",
+ "authority": null,
+ "checked": 0,
+ "isMenu": 0,
+ "parentId": -1
+ },
+ {
+ "id": 1,
+ "title": "系统管理",
+ "sort": 1,
+ "menuUrl": null,
+ "icon": "layui-icon-set",
+ "created_at": "2018/06/29 11:05:41",
+ "authority": null,
+ "checked": 0,
+ "isMenu": 0,
+ "parentId": -1
+ },
+ {
+ "id": 1,
+ "title": "系统管理",
+ "sort": 1,
+ "menuUrl": null,
+ "icon": "layui-icon-set",
+ "created_at": "2018/06/29 11:05:41",
+ "authority": null,
+ "checked": 0,
+ "isMenu": 0,
+ "parentId": -1
+ },
+ {
+ "id": 1,
+ "title": "系统管理",
+ "sort": 1,
+ "menuUrl": null,
+ "icon": "layui-icon-set",
+ "created_at": "2018/06/29 11:05:41",
+ "authority": null,
+ "checked": 0,
+ "isMenu": 0,
+ "parentId": -1
+ },{
+ "id": 1,
+ "title": "系统管理",
+ "sort": 1,
+ "menuUrl": null,
+ "icon": "layui-icon-set",
+ "created_at": "2018/06/29 11:05:41",
+ "authority": null,
+ "checked": 0,
+ "isMenu": 0,
+ "parentId": -1
+ },
+ {
+ "id": 1,
+ "title": "系统管理",
+ "sort": 1,
+ "menuUrl": null,
+ "icon": "layui-icon-set",
+ "created_at": "2018/06/29 11:05:41",
+ "authority": null,
+ "checked": 0,
+ "isMenu": 0,
+ "parentId": -1
+ },
+ {
+ "id": 1,
+ "title": "系统管理",
+ "sort": 1,
+ "menuUrl": null,
+ "icon": "layui-icon-set",
+ "created_at": "2018/06/29 11:05:41",
+ "authority": null,
+ "checked": 0,
+ "isMenu": 0,
+ "parentId": -1
+ },
+ {
+ "id": 1,
+ "title": "系统管理",
+ "sort": 1,
+ "menuUrl": null,
+ "icon": "layui-icon-set",
+ "created_at": "2018/06/29 11:05:41",
+ "authority": null,
+ "checked": 0,
+ "isMenu": 0,
+ "parentId": -1
+ }
+ ]
+}
\ No newline at end of file
diff --git a/public/static/api/menus.json b/public/static/api/menus.json
new file mode 100644
index 0000000..e14d00e
--- /dev/null
+++ b/public/static/api/menus.json
@@ -0,0 +1,254 @@
+{
+ "code": 0,
+ "msg": "",
+ "count": 19,
+ "data": [
+ {
+ "authorityId": 1,
+ "authorityName": "系统管理",
+ "orderNumber": 1,
+ "menuUrl": null,
+ "menuIcon": "layui-icon-set",
+ "createTime": "2018/06/29 11:05:41",
+ "authority": null,
+ "checked": 0,
+ "updateTime": "2018/07/13 09:13:42",
+ "isMenu": 0,
+ "parentId": -1
+ },
+ {
+ "authorityId": 2,
+ "authorityName": "用户管理",
+ "orderNumber": 2,
+ "menuUrl": "system/user",
+ "menuIcon": null,
+ "createTime": "2018/06/29 11:05:41",
+ "authority": null,
+ "checked": 0,
+ "updateTime": "2018/07/13 09:13:42",
+ "isMenu": 0,
+ "parentId": 1
+ },
+ {
+ "authorityId": 3,
+ "authorityName": "查询用户",
+ "orderNumber": 3,
+ "menuUrl": "",
+ "menuIcon": "",
+ "createTime": "2018/07/21 13:54:16",
+ "authority": "user:view",
+ "checked": 0,
+ "updateTime": "2018/07/21 13:54:16",
+ "isMenu": 1,
+ "parentId": 2
+ },
+ {
+ "authorityId": 4,
+ "authorityName": "添加用户",
+ "orderNumber": 4,
+ "menuUrl": null,
+ "menuIcon": null,
+ "createTime": "2018/06/29 11:05:41",
+ "authority": "user:add",
+ "checked": 0,
+ "updateTime": "2018/07/13 09:13:42",
+ "isMenu": 1,
+ "parentId": 2
+ },
+ {
+ "authorityId": 5,
+ "authorityName": "修改用户",
+ "orderNumber": 5,
+ "menuUrl": null,
+ "menuIcon": null,
+ "createTime": "2018/06/29 11:05:41",
+ "authority": "user:edit",
+ "checked": 0,
+ "updateTime": "2018/07/13 09:13:42",
+ "isMenu": 1,
+ "parentId": 2
+ },
+ {
+ "authorityId": 6,
+ "authorityName": "删除用户",
+ "orderNumber": 6,
+ "menuUrl": null,
+ "menuIcon": null,
+ "createTime": "2018/06/29 11:05:41",
+ "authority": "user:delete",
+ "checked": 0,
+ "updateTime": "2018/07/13 09:13:42",
+ "isMenu": 1,
+ "parentId": 2
+ },
+ {
+ "authorityId": 7,
+ "authorityName": "角色管理",
+ "orderNumber": 7,
+ "menuUrl": "system/role",
+ "menuIcon": null,
+ "createTime": "2018/06/29 11:05:41",
+ "authority": null,
+ "checked": 0,
+ "updateTime": "2018/07/13 09:13:42",
+ "isMenu": 0,
+ "parentId": 1
+ },
+ {
+ "authorityId": 8,
+ "authorityName": "查询角色",
+ "orderNumber": 8,
+ "menuUrl": "",
+ "menuIcon": "",
+ "createTime": "2018/07/21 13:54:59",
+ "authority": "role:view",
+ "checked": 0,
+ "updateTime": "2018/07/21 13:54:58",
+ "isMenu": 1,
+ "parentId": 7
+ },
+ {
+ "authorityId": 9,
+ "authorityName": "添加角色",
+ "orderNumber": 9,
+ "menuUrl": "",
+ "menuIcon": "",
+ "createTime": "2018/06/29 11:05:41",
+ "authority": "role:add",
+ "checked": 0,
+ "updateTime": "2018/07/13 09:13:42",
+ "isMenu": 1,
+ "parentId": 7
+ },
+ {
+ "authorityId": 10,
+ "authorityName": "修改角色",
+ "orderNumber": 10,
+ "menuUrl": "",
+ "menuIcon": "",
+ "createTime": "2018/06/29 11:05:41",
+ "authority": "role:edit",
+ "checked": 0,
+ "updateTime": "2018/07/13 09:13:42",
+ "isMenu": 1,
+ "parentId": 7
+ },
+ {
+ "authorityId": 11,
+ "authorityName": "删除角色",
+ "orderNumber": 11,
+ "menuUrl": "",
+ "menuIcon": "",
+ "createTime": "2018/06/29 11:05:41",
+ "authority": "role:delete",
+ "checked": 0,
+ "updateTime": "2018/07/13 09:13:42",
+ "isMenu": 1,
+ "parentId": 7
+ },
+ {
+ "authorityId": 12,
+ "authorityName": "角色权限管理",
+ "orderNumber": 12,
+ "menuUrl": "",
+ "menuIcon": "",
+ "createTime": "2018/06/29 11:05:41",
+ "authority": "role:auth",
+ "checked": 0,
+ "updateTime": "2018/07/13 15:27:18",
+ "isMenu": 1,
+ "parentId": 7
+ },
+ {
+ "authorityId": 13,
+ "authorityName": "权限管理",
+ "orderNumber": 13,
+ "menuUrl": "system/authorities",
+ "menuIcon": null,
+ "createTime": "2018/06/29 11:05:41",
+ "authority": null,
+ "checked": 0,
+ "updateTime": "2018/07/13 15:45:13",
+ "isMenu": 0,
+ "parentId": 1
+ },
+ {
+ "authorityId": 14,
+ "authorityName": "查询权限",
+ "orderNumber": 14,
+ "menuUrl": "",
+ "menuIcon": "",
+ "createTime": "2018/07/21 13:55:57",
+ "authority": "authorities:view",
+ "checked": 0,
+ "updateTime": "2018/07/21 13:55:56",
+ "isMenu": 1,
+ "parentId": 13
+ },
+ {
+ "authorityId": 15,
+ "authorityName": "添加权限",
+ "orderNumber": 15,
+ "menuUrl": "",
+ "menuIcon": "",
+ "createTime": "2018/06/29 11:05:41",
+ "authority": "authorities:add",
+ "checked": 0,
+ "updateTime": "2018/06/29 11:05:41",
+ "isMenu": 1,
+ "parentId": 13
+ },
+ {
+ "authorityId": 16,
+ "authorityName": "修改权限",
+ "orderNumber": 16,
+ "menuUrl": "",
+ "menuIcon": "",
+ "createTime": "2018/07/13 09:13:42",
+ "authority": "authorities:edit",
+ "checked": 0,
+ "updateTime": "2018/07/13 09:13:42",
+ "isMenu": 1,
+ "parentId": 13
+ },
+ {
+ "authorityId": 17,
+ "authorityName": "删除权限",
+ "orderNumber": 17,
+ "menuUrl": "",
+ "menuIcon": "",
+ "createTime": "2018/06/29 11:05:41",
+ "authority": "authorities:delete",
+ "checked": 0,
+ "updateTime": "2018/06/29 11:05:41",
+ "isMenu": 1,
+ "parentId": 13
+ },
+ {
+ "authorityId": 18,
+ "authorityName": "登录日志",
+ "orderNumber": 18,
+ "menuUrl": "system/loginRecord",
+ "menuIcon": null,
+ "createTime": "2018/06/29 11:05:41",
+ "authority": null,
+ "checked": 0,
+ "updateTime": "2018/06/29 11:05:41",
+ "isMenu": 0,
+ "parentId": 1
+ },
+ {
+ "authorityId": 19,
+ "authorityName": "查询登录日志",
+ "orderNumber": 19,
+ "menuUrl": "",
+ "menuIcon": "",
+ "createTime": "2018/07/21 13:56:43",
+ "authority": "loginRecord:view",
+ "checked": 0,
+ "updateTime": "2018/07/21 13:56:43",
+ "isMenu": 1,
+ "parentId": 18
+ }
+ ]
+}
\ No newline at end of file
diff --git a/public/static/api/table.json b/public/static/api/table.json
new file mode 100644
index 0000000..7bda61b
--- /dev/null
+++ b/public/static/api/table.json
@@ -0,0 +1,127 @@
+{
+ "code": 0,
+ "msg": "",
+ "count": 1000,
+ "data": [
+ {
+ "id": 10000,
+ "username": "user-0",
+ "sex": "女",
+ "city": "城市-0",
+ "sign": "签名-0",
+ "experience": 255,
+ "logins": 24,
+ "wealth": 82830700,
+ "classify": "作家",
+ "score": 57
+ },
+ {
+ "id": 10001,
+ "username": "user-1",
+ "sex": "男",
+ "city": "城市-1",
+ "sign": "签名-1",
+ "experience": 884,
+ "logins": 58,
+ "wealth": 64928690,
+ "classify": "词人",
+ "score": 27
+ },
+ {
+ "id": 10002,
+ "username": "user-2",
+ "sex": "女",
+ "city": "城市-2",
+ "sign": "签名-2",
+ "experience": 650,
+ "logins": 77,
+ "wealth": 6298078,
+ "classify": "酱油",
+ "score": 31
+ },
+ {
+ "id": 10003,
+ "username": "user-3",
+ "sex": "女",
+ "city": "城市-3",
+ "sign": "签名-3",
+ "experience": 362,
+ "logins": 157,
+ "wealth": 37117017,
+ "classify": "诗人",
+ "score": 68
+ },
+ {
+ "id": 10004,
+ "username": "user-4",
+ "sex": "男",
+ "city": "城市-4",
+ "sign": "签名-4",
+ "experience": 807,
+ "logins": 51,
+ "wealth": 76263262,
+ "classify": "作家",
+ "score": 6
+ },
+ {
+ "id": 10005,
+ "username": "user-5",
+ "sex": "女",
+ "city": "城市-5",
+ "sign": "签名-5",
+ "experience": 173,
+ "logins": 68,
+ "wealth": 60344147,
+ "classify": "作家",
+ "score": 87
+ },
+ {
+ "id": 10006,
+ "username": "user-6",
+ "sex": "女",
+ "city": "城市-6",
+ "sign": "签名-6",
+ "experience": 982,
+ "logins": 37,
+ "wealth": 57768166,
+ "classify": "作家",
+ "score": 34
+ },
+ {
+ "id": 10007,
+ "username": "user-7",
+ "sex": "男",
+ "city": "城市-7",
+ "sign": "签名-7",
+ "experience": 727,
+ "logins": 150,
+ "wealth": 82030578,
+ "classify": "作家",
+ "score": 28
+ },
+ {
+ "id": 10008,
+ "username": "user-8",
+ "sex": "男",
+ "city": "城市-8",
+ "sign": "签名-8",
+ "experience": 951,
+ "logins": 133,
+ "wealth": 16503371,
+ "classify": "词人",
+ "score": 14
+ },
+ {
+ "id": 10009,
+ "username": "user-9",
+ "sex": "女",
+ "city": "城市-9",
+ "sign": "签名-9",
+ "experience": 484,
+ "logins": 25,
+ "wealth": 86801934,
+ "classify": "词人",
+ "score": 75
+ }
+ ]
+}
\ No newline at end of file
diff --git a/public/static/api/tableSelect.json b/public/static/api/tableSelect.json
new file mode 100644
index 0000000..37fb0ed
--- /dev/null
+++ b/public/static/api/tableSelect.json
@@ -0,0 +1,23 @@
+{
+ "code": 0,
+ "msg": "",
+ "count": 16,
+ "data": [
+ { "id":"001", "username":"张玉林", "sex":"女" },
+ { "id":"002", "username":"刘晓军", "sex":"男" },
+ { "id":"003", "username":"张恒", "sex":"男" },
+ { "id":"004", "username":"朱一", "sex":"男" },
+ { "id":"005", "username":"刘佳能", "sex":"女" },
+ { "id":"006", "username":"晓梅", "sex":"女" },
+ { "id":"007", "username":"马冬梅", "sex":"女" },
+ { "id":"008", "username":"刘晓庆", "sex":"女" },
+ { "id":"009", "username":"刘晓庆", "sex":"女" },
+ { "id":"010", "username":"刘晓庆", "sex":"女" },
+ { "id":"011", "username":"刘晓庆", "sex":"女" },
+ { "id":"012", "username":"刘晓庆", "sex":"女" },
+ { "id":"013", "username":"刘晓庆", "sex":"女" },
+ { "id":"014", "username":"刘晓庆", "sex":"女" },
+ { "id":"015", "username":"刘晓庆", "sex":"女" },
+ { "id":"016", "username":"刘晓庆", "sex":"女" }
+ ]
+}
\ No newline at end of file
diff --git a/public/static/api/upload.json b/public/static/api/upload.json
new file mode 100644
index 0000000..691902d
--- /dev/null
+++ b/public/static/api/upload.json
@@ -0,0 +1,10 @@
+{
+ "code": 1,
+ "msg": "上传成功",
+ "data": {
+ "url": [
+ "../images/logo.png",
+ "../images/captcha.jpg"
+ ]
+ }
+}
diff --git a/public/static/images/bg-upload.png b/public/static/images/bg-upload.png
new file mode 100644
index 0000000..de827d6
Binary files /dev/null and b/public/static/images/bg-upload.png differ
diff --git a/public/static/images/icon-logo.jpg b/public/static/images/icon-logo.jpg
new file mode 100644
index 0000000..c979f27
Binary files /dev/null and b/public/static/images/icon-logo.jpg differ
diff --git a/public/static/images/poster-bg1.png b/public/static/images/poster-bg1.png
new file mode 100644
index 0000000..4e53c82
Binary files /dev/null and b/public/static/images/poster-bg1.png differ
diff --git a/public/static/images/scan.jpeg b/public/static/images/scan.jpeg
new file mode 100644
index 0000000..9439f3f
Binary files /dev/null and b/public/static/images/scan.jpeg differ
diff --git a/public/static/js/city-picker.data.js b/public/static/js/city-picker.data.js
new file mode 100644
index 0000000..148f45c
--- /dev/null
+++ b/public/static/js/city-picker.data.js
@@ -0,0 +1,4058 @@
+/*!
+ * Distpicker v1.0.2
+ * https://github.com/tshi0912/city-picker
+ *
+ * Copyright (c) 2014-2016 Tao Shi
+ * Released under the MIT license
+ *
+ * Date: 2016-02-29T12:11:36.473Z
+ */
+
+function districts() {
+
+ var ChineseDistricts = {
+ 86: {
+ 110000: '北京',
+ 120000: '天津',
+ 130000: '河北',
+ 140000: '山西',
+ 150000: '内蒙古',
+ 210000: '辽宁',
+ 220000: '吉林',
+ 230000: '黑龙江',
+ 310000: '上海',
+ 320000: '江苏',
+ 330000: '浙江',
+ 340000: '安徽',
+ 350000: '福建',
+ 360000: '江西',
+ 370000: '山东',
+ 410000: '河南',
+ 420000: '湖北',
+ 430000: '湖南',
+ 440000: '广东',
+ 450000: '广西',
+ 460000: '海南',
+ 500000: '重庆',
+ 510000: '四川',
+ 520000: '贵州',
+ 530000: '云南',
+ 540000: '西藏',
+ 610000: '陕西',
+ 620000: '甘肃',
+ 630000: '青海',
+ 640000: '宁夏',
+ 650000: '新疆',
+ 710000: '台湾',
+ 810000: '香港',
+ 820000: '澳门'
+ },
+ // 86: {
+ // 'A-G': [
+ // {code: '340000', address: '安徽省'},
+ // {code: '110000', address: '北京市'},
+ // {code: '500000', address: '重庆市'},
+ // {code: '350000', address: '福建省'},
+ // {code: '620000', address: '甘肃省'},
+ // {code: '440000', address: '广东省'},
+ // {code: '450000', address: '广西壮族自治区'},
+ // {code: '520000', address: '贵州省'}],
+ // 'H-K': [
+ // {code: '460000', address: '海南省'},
+ // {code: '130000', address: '河北省'},
+ // {code: '230000', address: '黑龙江省'},
+ // {code: '410000', address: '河南省'},
+ // {code: '420000', address: '湖北省'},
+ // {code: '430000', address: '湖南省'},
+ // {code: '320000', address: '江苏省'},
+ // {code: '360000', address: '江西省'},
+ // {code: '220000', address: '吉林省'}],
+ // 'L-S': [
+ // {code: '210000', address: '辽宁省'},
+ // {code: '150000', address: '内蒙古自治区'},
+ // {code: '640000', address: '宁夏回族自治区'},
+ // {code: '630000', address: '青海省'},
+ // {code: '370000', address: '山东省'},
+ // {code: '310000', address: '上海市'},
+ // {code: '140000', address: '山西省'},
+ // {code: '610000', address: '陕西省'},
+ // {code: '510000', address: '四川省'}],
+ // 'T-Z': [
+ // {code: '120000', address: '天津市'},
+ // {code: '650000', address: '新疆维吾尔自治区'},
+ // {code: '540000', address: '西藏自治区'},
+ // {code: '530000', address: '云南省'},
+ // {code: '330000', address: '浙江省'}]
+ // },
+ 110000: {
+ 110100: '北京市',
+ },
+ 110100: {
+ 110101: '东城区',
+ 110102: '西城区',
+ 110105: '朝阳区',
+ 110106: '丰台区',
+ 110107: '石景山区',
+ 110108: '海淀区',
+ 110109: '门头沟区',
+ 110111: '房山区',
+ 110112: '通州区',
+ 110113: '顺义区',
+ 110114: '昌平区',
+ 110115: '大兴区',
+ 110116: '怀柔区',
+ 110117: '平谷区',
+ 110228: '密云县',
+ 110229: '延庆县'
+ },
+ 120000: {
+ 120100: '天津市'
+ },
+ 120100: {
+ 120101: '和平区',
+ 120102: '河东区',
+ 120103: '河西区',
+ 120104: '南开区',
+ 120105: '河北区',
+ 120106: '红桥区',
+ 120110: '东丽区',
+ 120111: '西青区',
+ 120112: '津南区',
+ 120113: '北辰区',
+ 120114: '武清区',
+ 120115: '宝坻区',
+ 120116: '滨海新区',
+ 120221: '宁河县',
+ 120223: '静海县',
+ 120225: '蓟县'
+ },
+ 130000: {
+ 130100: '石家庄市',
+ 130200: '唐山市',
+ 130300: '秦皇岛市',
+ 130400: '邯郸市',
+ 130500: '邢台市',
+ 130600: '保定市',
+ 130700: '张家口市',
+ 130800: '承德市',
+ 130900: '沧州市',
+ 131000: '廊坊市',
+ 131100: '衡水市'
+ },
+ 130100: {
+ 130102: '长安区',
+ 130104: '桥西区',
+ 130105: '新华区',
+ 130107: '井陉矿区',
+ 130108: '裕华区',
+ 130109: '藁城区',
+ 130110: '鹿泉区',
+ 130111: '栾城区',
+ 130121: '井陉县',
+ 130123: '正定县',
+ 130125: '行唐县',
+ 130126: '灵寿县',
+ 130127: '高邑县',
+ 130128: '深泽县',
+ 130129: '赞皇县',
+ 130130: '无极县',
+ 130131: '平山县',
+ 130132: '元氏县',
+ 130133: '赵县',
+ 130181: '辛集市',
+ 130183: '晋州市',
+ 130184: '新乐市'
+ },
+ 130200: {
+ 130202: '路南区',
+ 130203: '路北区',
+ 130204: '古冶区',
+ 130205: '开平区',
+ 130207: '丰南区',
+ 130208: '丰润区',
+ 130209: '曹妃甸区',
+ 130223: '滦县',
+ 130224: '滦南县',
+ 130225: '乐亭县',
+ 130227: '迁西县',
+ 130229: '玉田县',
+ 130281: '遵化市',
+ 130283: '迁安市'
+ },
+ 130300: {
+ 130302: '海港区',
+ 130303: '山海关区',
+ 130304: '北戴河区',
+ 130321: '青龙满族自治县',
+ 130322: '昌黎县',
+ 130323: '抚宁县',
+ 130324: '卢龙县'
+ },
+ 130400: {
+ 130402: '邯山区',
+ 130403: '丛台区',
+ 130404: '复兴区',
+ 130406: '峰峰矿区',
+ 130421: '邯郸县',
+ 130423: '临漳县',
+ 130424: '成安县',
+ 130425: '大名县',
+ 130426: '涉县',
+ 130427: '磁县',
+ 130428: '肥乡县',
+ 130429: '永年县',
+ 130430: '邱县',
+ 130431: '鸡泽县',
+ 130432: '广平县',
+ 130433: '馆陶县',
+ 130434: '魏县',
+ 130435: '曲周县',
+ 130481: '武安市'
+ },
+ 130500: {
+ 130502: '桥东区',
+ 130503: '桥西区',
+ 130521: '邢台县',
+ 130522: '临城县',
+ 130523: '内丘县',
+ 130524: '柏乡县',
+ 130525: '隆尧县',
+ 130526: '任县',
+ 130527: '南和县',
+ 130528: '宁晋县',
+ 130529: '巨鹿县',
+ 130530: '新河县',
+ 130531: '广宗县',
+ 130532: '平乡县',
+ 130533: '威县',
+ 130534: '清河县',
+ 130535: '临西县',
+ 130581: '南宫市',
+ 130582: '沙河市'
+ },
+ 130600: {
+ 130602: '竞秀区',
+ 130603: '莲池区',
+ 130621: '满城区',
+ 130622: '清苑区',
+ 130623: '涞水县',
+ 130624: '阜平县',
+ 130625: '徐水区',
+ 130626: '定兴县',
+ 130627: '唐县',
+ 130628: '高阳县',
+ 130629: '容城县',
+ 130630: '涞源县',
+ 130631: '望都县',
+ 130632: '安新县',
+ 130633: '易县',
+ 130634: '曲阳县',
+ 130635: '蠡县',
+ 130636: '顺平县',
+ 130637: '博野县',
+ 130638: '雄县',
+ 130681: '涿州市',
+ 130682: '定州市',
+ 130683: '安国市',
+ 130684: '高碑店市'
+ },
+ 130700: {
+ 130702: '桥东区',
+ 130703: '桥西区',
+ 130705: '宣化区',
+ 130706: '下花园区',
+ 130721: '宣化县',
+ 130722: '张北县',
+ 130723: '康保县',
+ 130724: '沽源县',
+ 130725: '尚义县',
+ 130726: '蔚县',
+ 130727: '阳原县',
+ 130728: '怀安县',
+ 130729: '万全县',
+ 130730: '怀来县',
+ 130731: '涿鹿县',
+ 130732: '赤城县',
+ 130733: '崇礼县'
+ },
+ 130800: {
+ 130802: '双桥区',
+ 130803: '双滦区',
+ 130804: '鹰手营子矿区',
+ 130821: '承德县',
+ 130822: '兴隆县',
+ 130823: '平泉县',
+ 130824: '滦平县',
+ 130825: '隆化县',
+ 130826: '丰宁满族自治县',
+ 130827: '宽城满族自治县',
+ 130828: '围场满族蒙古族自治县'
+ },
+ 130900: {
+ 130902: '新华区',
+ 130903: '运河区',
+ 130921: '沧县',
+ 130922: '青县',
+ 130923: '东光县',
+ 130924: '海兴县',
+ 130925: '盐山县',
+ 130926: '肃宁县',
+ 130927: '南皮县',
+ 130928: '吴桥县',
+ 130929: '献县',
+ 130930: '孟村回族自治县',
+ 130981: '泊头市',
+ 130982: '任丘市',
+ 130983: '黄骅市',
+ 130984: '河间市'
+ },
+ 131000: {
+ 131002: '安次区',
+ 131003: '广阳区',
+ 131022: '固安县',
+ 131023: '永清县',
+ 131024: '香河县',
+ 131025: '大城县',
+ 131026: '文安县',
+ 131028: '大厂回族自治县',
+ 131081: '霸州市',
+ 131082: '三河市'
+ },
+ 131100: {
+ 131102: '桃城区',
+ 131121: '枣强县',
+ 131122: '武邑县',
+ 131123: '武强县',
+ 131124: '饶阳县',
+ 131125: '安平县',
+ 131126: '故城县',
+ 131127: '景县',
+ 131128: '阜城县',
+ 131181: '冀州市',
+ 131182: '深州市'
+ },
+ 140000: {
+ 140100: '太原市',
+ 140200: '大同市',
+ 140300: '阳泉市',
+ 140400: '长治市',
+ 140500: '晋城市',
+ 140600: '朔州市',
+ 140700: '晋中市',
+ 140800: '运城市',
+ 140900: '忻州市',
+ 141000: '临汾市',
+ 141100: '吕梁市'
+ },
+ 140100: {
+ 140105: '小店区',
+ 140106: '迎泽区',
+ 140107: '杏花岭区',
+ 140108: '尖草坪区',
+ 140109: '万柏林区',
+ 140110: '晋源区',
+ 140121: '清徐县',
+ 140122: '阳曲县',
+ 140123: '娄烦县',
+ 140181: '古交市'
+ },
+ 140200: {
+ 140202: '城区',
+ 140203: '矿区',
+ 140211: '南郊区',
+ 140212: '新荣区',
+ 140221: '阳高县',
+ 140222: '天镇县',
+ 140223: '广灵县',
+ 140224: '灵丘县',
+ 140225: '浑源县',
+ 140226: '左云县',
+ 140227: '大同县'
+ },
+ 140300: {
+ 140302: '城区',
+ 140303: '矿区',
+ 140311: '郊区',
+ 140321: '平定县',
+ 140322: '盂县'
+ },
+ 140400: {
+ 140402: '城区',
+ 140411: '郊区',
+ 140421: '长治县',
+ 140423: '襄垣县',
+ 140424: '屯留县',
+ 140425: '平顺县',
+ 140426: '黎城县',
+ 140427: '壶关县',
+ 140428: '长子县',
+ 140429: '武乡县',
+ 140430: '沁县',
+ 140431: '沁源县',
+ 140481: '潞城市'
+ },
+ 140500: {
+ 140502: '城区',
+ 140521: '沁水县',
+ 140522: '阳城县',
+ 140524: '陵川县',
+ 140525: '泽州县',
+ 140581: '高平市'
+ },
+ 140600: {
+ 140602: '朔城区',
+ 140603: '平鲁区',
+ 140621: '山阴县',
+ 140622: '应县',
+ 140623: '右玉县',
+ 140624: '怀仁县'
+ },
+ 140700: {
+ 140702: '榆次区',
+ 140721: '榆社县',
+ 140722: '左权县',
+ 140723: '和顺县',
+ 140724: '昔阳县',
+ 140725: '寿阳县',
+ 140726: '太谷县',
+ 140727: '祁县',
+ 140728: '平遥县',
+ 140729: '灵石县',
+ 140781: '介休市'
+ },
+ 140800: {
+ 140802: '盐湖区',
+ 140821: '临猗县',
+ 140822: '万荣县',
+ 140823: '闻喜县',
+ 140824: '稷山县',
+ 140825: '新绛县',
+ 140826: '绛县',
+ 140827: '垣曲县',
+ 140828: '夏县',
+ 140829: '平陆县',
+ 140830: '芮城县',
+ 140881: '永济市',
+ 140882: '河津市'
+ },
+ 140900: {
+ 140902: '忻府区',
+ 140921: '定襄县',
+ 140922: '五台县',
+ 140923: '代县',
+ 140924: '繁峙县',
+ 140925: '宁武县',
+ 140926: '静乐县',
+ 140927: '神池县',
+ 140928: '五寨县',
+ 140929: '岢岚县',
+ 140930: '河曲县',
+ 140931: '保德县',
+ 140932: '偏关县',
+ 140981: '原平市'
+ },
+ 141000: {
+ 141002: '尧都区',
+ 141021: '曲沃县',
+ 141022: '翼城县',
+ 141023: '襄汾县',
+ 141024: '洪洞县',
+ 141025: '古县',
+ 141026: '安泽县',
+ 141027: '浮山县',
+ 141028: '吉县',
+ 141029: '乡宁县',
+ 141030: '大宁县',
+ 141031: '隰县',
+ 141032: '永和县',
+ 141033: '蒲县',
+ 141034: '汾西县',
+ 141081: '侯马市',
+ 141082: '霍州市'
+ },
+ 141100: {
+ 141102: '离石区',
+ 141121: '文水县',
+ 141122: '交城县',
+ 141123: '兴县',
+ 141124: '临县',
+ 141125: '柳林县',
+ 141126: '石楼县',
+ 141127: '岚县',
+ 141128: '方山县',
+ 141129: '中阳县',
+ 141130: '交口县',
+ 141181: '孝义市',
+ 141182: '汾阳市'
+ },
+ 150000: {
+ 150100: '呼和浩特市',
+ 150200: '包头市',
+ 150300: '乌海市',
+ 150400: '赤峰市',
+ 150500: '通辽市',
+ 150600: '鄂尔多斯市',
+ 150700: '呼伦贝尔市',
+ 150800: '巴彦淖尔市',
+ 150900: '乌兰察布市',
+ 152200: '兴安盟',
+ 152500: '锡林郭勒盟',
+ 152900: '阿拉善盟'
+ },
+ 150100: {
+ 150102: '新城区',
+ 150103: '回民区',
+ 150104: '玉泉区',
+ 150105: '赛罕区',
+ 150121: '土默特左旗',
+ 150122: '托克托县',
+ 150123: '和林格尔县',
+ 150124: '清水河县',
+ 150125: '武川县'
+ },
+ 150200: {
+ 150202: '东河区',
+ 150203: '昆都仑区',
+ 150204: '青山区',
+ 150205: '石拐区',
+ 150206: '白云鄂博矿区',
+ 150207: '九原区',
+ 150221: '土默特右旗',
+ 150222: '固阳县',
+ 150223: '达尔罕茂明安联合旗'
+ },
+ 150300: {
+ 150302: '海勃湾区',
+ 150303: '海南区',
+ 150304: '乌达区'
+ },
+ 150400: {
+ 150402: '红山区',
+ 150403: '元宝山区',
+ 150404: '松山区',
+ 150421: '阿鲁科尔沁旗',
+ 150422: '巴林左旗',
+ 150423: '巴林右旗',
+ 150424: '林西县',
+ 150425: '克什克腾旗',
+ 150426: '翁牛特旗',
+ 150428: '喀喇沁旗',
+ 150429: '宁城县',
+ 150430: '敖汉旗'
+ },
+ 150500: {
+ 150502: '科尔沁区',
+ 150521: '科尔沁左翼中旗',
+ 150522: '科尔沁左翼后旗',
+ 150523: '开鲁县',
+ 150524: '库伦旗',
+ 150525: '奈曼旗',
+ 150526: '扎鲁特旗',
+ 150581: '霍林郭勒市'
+ },
+ 150600: {
+ 150602: '东胜区',
+ 150621: '达拉特旗',
+ 150622: '准格尔旗',
+ 150623: '鄂托克前旗',
+ 150624: '鄂托克旗',
+ 150625: '杭锦旗',
+ 150626: '乌审旗',
+ 150627: '伊金霍洛旗'
+ },
+ 150700: {
+ 150702: '海拉尔区',
+ 150703: '扎赉诺尔区',
+ 150721: '阿荣旗',
+ 150722: '莫力达瓦达斡尔族自治旗',
+ 150723: '鄂伦春自治旗',
+ 150724: '鄂温克族自治旗',
+ 150725: '陈巴尔虎旗',
+ 150726: '新巴尔虎左旗',
+ 150727: '新巴尔虎右旗',
+ 150781: '满洲里市',
+ 150782: '牙克石市',
+ 150783: '扎兰屯市',
+ 150784: '额尔古纳市',
+ 150785: '根河市'
+ },
+ 150800: {
+ 150802: '临河区',
+ 150821: '五原县',
+ 150822: '磴口县',
+ 150823: '乌拉特前旗',
+ 150824: '乌拉特中旗',
+ 150825: '乌拉特后旗',
+ 150826: '杭锦后旗'
+ },
+ 150900: {
+ 150902: '集宁区',
+ 150921: '卓资县',
+ 150922: '化德县',
+ 150923: '商都县',
+ 150924: '兴和县',
+ 150925: '凉城县',
+ 150926: '察哈尔右翼前旗',
+ 150927: '察哈尔右翼中旗',
+ 150928: '察哈尔右翼后旗',
+ 150929: '四子王旗',
+ 150981: '丰镇市'
+ },
+ 152200: {
+ 152201: '乌兰浩特市',
+ 152202: '阿尔山市',
+ 152221: '科尔沁右翼前旗',
+ 152222: '科尔沁右翼中旗',
+ 152223: '扎赉特旗',
+ 152224: '突泉县'
+ },
+ 152500: {
+ 152501: '二连浩特市',
+ 152502: '锡林浩特市',
+ 152522: '阿巴嘎旗',
+ 152523: '苏尼特左旗',
+ 152524: '苏尼特右旗',
+ 152525: '东乌珠穆沁旗',
+ 152526: '西乌珠穆沁旗',
+ 152527: '太仆寺旗',
+ 152528: '镶黄旗',
+ 152529: '正镶白旗',
+ 152530: '正蓝旗',
+ 152531: '多伦县'
+ },
+ 152900: {
+ 152921: '阿拉善左旗',
+ 152922: '阿拉善右旗',
+ 152923: '额济纳旗'
+ },
+ 210000: {
+ 210100: '沈阳市',
+ 210200: '大连市',
+ 210300: '鞍山市',
+ 210400: '抚顺市',
+ 210500: '本溪市',
+ 210600: '丹东市',
+ 210700: '锦州市',
+ 210800: '营口市',
+ 210900: '阜新市',
+ 211000: '辽阳市',
+ 211100: '盘锦市',
+ 211200: '铁岭市',
+ 211300: '朝阳市',
+ 211400: '葫芦岛市'
+ },
+ 210100: {
+ 210102: '和平区',
+ 210103: '沈河区',
+ 210104: '大东区',
+ 210105: '皇姑区',
+ 210106: '铁西区',
+ 210111: '苏家屯区',
+ 210112: '浑南区',
+ 210113: '沈北新区',
+ 210114: '于洪区',
+ 210122: '辽中县',
+ 210123: '康平县',
+ 210124: '法库县',
+ 210181: '新民市'
+ },
+ 210200: {
+ 210202: '中山区',
+ 210203: '西岗区',
+ 210204: '沙河口区',
+ 210211: '甘井子区',
+ 210212: '旅顺口区',
+ 210213: '金州区',
+ 210224: '长海县',
+ 210281: '瓦房店市',
+ 210282: '普兰店市',
+ 210283: '庄河市'
+ },
+ 210300: {
+ 210302: '铁东区',
+ 210303: '铁西区',
+ 210304: '立山区',
+ 210311: '千山区',
+ 210321: '台安县',
+ 210323: '岫岩满族自治县',
+ 210381: '海城市'
+ },
+ 210400: {
+ 210402: '新抚区',
+ 210403: '东洲区',
+ 210404: '望花区',
+ 210411: '顺城区',
+ 210421: '抚顺县',
+ 210422: '新宾满族自治县',
+ 210423: '清原满族自治县'
+ },
+ 210500: {
+ 210502: '平山区',
+ 210503: '溪湖区',
+ 210504: '明山区',
+ 210505: '南芬区',
+ 210521: '本溪满族自治县',
+ 210522: '桓仁满族自治县'
+ },
+ 210600: {
+ 210602: '元宝区',
+ 210603: '振兴区',
+ 210604: '振安区',
+ 210624: '宽甸满族自治县',
+ 210681: '东港市',
+ 210682: '凤城市'
+ },
+ 210700: {
+ 210702: '古塔区',
+ 210703: '凌河区',
+ 210711: '太和区',
+ 210726: '黑山县',
+ 210727: '义县',
+ 210781: '凌海市',
+ 210782: '北镇市'
+ },
+ 210800: {
+ 210802: '站前区',
+ 210803: '西市区',
+ 210804: '鲅鱼圈区',
+ 210811: '老边区',
+ 210881: '盖州市',
+ 210882: '大石桥市'
+ },
+ 210900: {
+ 210902: '海州区',
+ 210903: '新邱区',
+ 210904: '太平区',
+ 210905: '清河门区',
+ 210911: '细河区',
+ 210921: '阜新蒙古族自治县',
+ 210922: '彰武县'
+ },
+ 211000: {
+ 211002: '白塔区',
+ 211003: '文圣区',
+ 211004: '宏伟区',
+ 211005: '弓长岭区',
+ 211011: '太子河区',
+ 211021: '辽阳县',
+ 211081: '灯塔市'
+ },
+ 211100: {
+ 211102: '双台子区',
+ 211103: '兴隆台区',
+ 211121: '大洼县',
+ 211122: '盘山县'
+ },
+ 211200: {
+ 211202: '银州区',
+ 211204: '清河区',
+ 211221: '铁岭县',
+ 211223: '西丰县',
+ 211224: '昌图县',
+ 211281: '调兵山市',
+ 211282: '开原市'
+ },
+ 211300: {
+ 211302: '双塔区',
+ 211303: '龙城区',
+ 211321: '朝阳县',
+ 211322: '建平县',
+ 211324: '喀喇沁左翼蒙古族自治县',
+ 211381: '北票市',
+ 211382: '凌源市'
+ },
+ 211400: {
+ 211402: '连山区',
+ 211403: '龙港区',
+ 211404: '南票区',
+ 211421: '绥中县',
+ 211422: '建昌县',
+ 211481: '兴城市'
+ },
+ 220000: {
+ 220100: '长春市',
+ 220200: '吉林市',
+ 220300: '四平市',
+ 220400: '辽源市',
+ 220500: '通化市',
+ 220600: '白山市',
+ 220700: '松原市',
+ 220800: '白城市',
+ 222400: '延边朝鲜族自治州'
+ },
+ 220100: {
+ 220102: '南关区',
+ 220103: '宽城区',
+ 220104: '朝阳区',
+ 220105: '二道区',
+ 220106: '绿园区',
+ 220112: '双阳区',
+ 220113: '九台区',
+ 220122: '农安县',
+ 220182: '榆树市',
+ 220183: '德惠市'
+ },
+ 220200: {
+ 220202: '昌邑区',
+ 220203: '龙潭区',
+ 220204: '船营区',
+ 220211: '丰满区',
+ 220221: '永吉县',
+ 220281: '蛟河市',
+ 220282: '桦甸市',
+ 220283: '舒兰市',
+ 220284: '磐石市'
+ },
+ 220300: {
+ 220302: '铁西区',
+ 220303: '铁东区',
+ 220322: '梨树县',
+ 220323: '伊通满族自治县',
+ 220381: '公主岭市',
+ 220382: '双辽市'
+ },
+ 220400: {
+ 220402: '龙山区',
+ 220403: '西安区',
+ 220421: '东丰县',
+ 220422: '东辽县'
+ },
+ 220500: {
+ 220502: '东昌区',
+ 220503: '二道江区',
+ 220521: '通化县',
+ 220523: '辉南县',
+ 220524: '柳河县',
+ 220581: '梅河口市',
+ 220582: '集安市'
+ },
+ 220600: {
+ 220602: '浑江区',
+ 220605: '江源区',
+ 220621: '抚松县',
+ 220622: '靖宇县',
+ 220623: '长白朝鲜族自治县',
+ 220681: '临江市'
+ },
+ 220700: {
+ 220702: '宁江区',
+ 220721: '前郭尔罗斯蒙古族自治县',
+ 220722: '长岭县',
+ 220723: '乾安县',
+ 220781: '扶余市'
+ },
+ 220800: {
+ 220802: '洮北区',
+ 220821: '镇赉县',
+ 220822: '通榆县',
+ 220881: '洮南市',
+ 220882: '大安市'
+ },
+ 222400: {
+ 222401: '延吉市',
+ 222402: '图们市',
+ 222403: '敦化市',
+ 222404: '珲春市',
+ 222405: '龙井市',
+ 222406: '和龙市',
+ 222424: '汪清县',
+ 222426: '安图县'
+ },
+ 230000: {
+ 230100: '哈尔滨市',
+ 230200: '齐齐哈尔市',
+ 230300: '鸡西市',
+ 230400: '鹤岗市',
+ 230500: '双鸭山市',
+ 230600: '大庆市',
+ 230700: '伊春市',
+ 230800: '佳木斯市',
+ 230900: '七台河市',
+ 231000: '牡丹江市',
+ 231100: '黑河市',
+ 231200: '绥化市',
+ 232700: '大兴安岭地区'
+ },
+ 230100: {
+ 230102: '道里区',
+ 230103: '南岗区',
+ 230104: '道外区',
+ 230108: '平房区',
+ 230109: '松北区',
+ 230110: '香坊区',
+ 230111: '呼兰区',
+ 230112: '阿城区',
+ 230113: '双城区',
+ 230123: '依兰县',
+ 230124: '方正县',
+ 230125: '宾县',
+ 230126: '巴彦县',
+ 230127: '木兰县',
+ 230128: '通河县',
+ 230129: '延寿县',
+ 230183: '尚志市',
+ 230184: '五常市'
+ },
+ 230200: {
+ 230202: '龙沙区',
+ 230203: '建华区',
+ 230204: '铁锋区',
+ 230205: '昂昂溪区',
+ 230206: '富拉尔基区',
+ 230207: '碾子山区',
+ 230208: '梅里斯达斡尔族区',
+ 230221: '龙江县',
+ 230223: '依安县',
+ 230224: '泰来县',
+ 230225: '甘南县',
+ 230227: '富裕县',
+ 230229: '克山县',
+ 230230: '克东县',
+ 230231: '拜泉县',
+ 230281: '讷河市'
+ },
+ 230300: {
+ 230302: '鸡冠区',
+ 230303: '恒山区',
+ 230304: '滴道区',
+ 230305: '梨树区',
+ 230306: '城子河区',
+ 230307: '麻山区',
+ 230321: '鸡东县',
+ 230381: '虎林市',
+ 230382: '密山市'
+ },
+ 230400: {
+ 230402: '向阳区',
+ 230403: '工农区',
+ 230404: '南山区',
+ 230405: '兴安区',
+ 230406: '东山区',
+ 230407: '兴山区',
+ 230421: '萝北县',
+ 230422: '绥滨县'
+ },
+ 230500: {
+ 230502: '尖山区',
+ 230503: '岭东区',
+ 230505: '四方台区',
+ 230506: '宝山区',
+ 230521: '集贤县',
+ 230522: '友谊县',
+ 230523: '宝清县',
+ 230524: '饶河县'
+ },
+ 230600: {
+ 230602: '萨尔图区',
+ 230603: '龙凤区',
+ 230604: '让胡路区',
+ 230605: '红岗区',
+ 230606: '大同区',
+ 230621: '肇州县',
+ 230622: '肇源县',
+ 230623: '林甸县',
+ 230624: '杜尔伯特蒙古族自治县'
+ },
+ 230700: {
+ 230702: '伊春区',
+ 230703: '南岔区',
+ 230704: '友好区',
+ 230705: '西林区',
+ 230706: '翠峦区',
+ 230707: '新青区',
+ 230708: '美溪区',
+ 230709: '金山屯区',
+ 230710: '五营区',
+ 230711: '乌马河区',
+ 230712: '汤旺河区',
+ 230713: '带岭区',
+ 230714: '乌伊岭区',
+ 230715: '红星区',
+ 230716: '上甘岭区',
+ 230722: '嘉荫县',
+ 230781: '铁力市'
+ },
+ 230800: {
+ 230803: '向阳区',
+ 230804: '前进区',
+ 230805: '东风区',
+ 230811: '郊区',
+ 230822: '桦南县',
+ 230826: '桦川县',
+ 230828: '汤原县',
+ 230833: '抚远县',
+ 230881: '同江市',
+ 230882: '富锦市'
+ },
+ 230900: {
+ 230902: '新兴区',
+ 230903: '桃山区',
+ 230904: '茄子河区',
+ 230921: '勃利县'
+ },
+ 231000: {
+ 231002: '东安区',
+ 231003: '阳明区',
+ 231004: '爱民区',
+ 231005: '西安区',
+ 231024: '东宁县',
+ 231025: '林口县',
+ 231081: '绥芬河市',
+ 231083: '海林市',
+ 231084: '宁安市',
+ 231085: '穆棱市'
+ },
+ 231100: {
+ 231102: '爱辉区',
+ 231121: '嫩江县',
+ 231123: '逊克县',
+ 231124: '孙吴县',
+ 231181: '北安市',
+ 231182: '五大连池市'
+ },
+ 231200: {
+ 231202: '北林区',
+ 231221: '望奎县',
+ 231222: '兰西县',
+ 231223: '青冈县',
+ 231224: '庆安县',
+ 231225: '明水县',
+ 231226: '绥棱县',
+ 231281: '安达市',
+ 231282: '肇东市',
+ 231283: '海伦市'
+ },
+ 232700: {
+ 232701: '加格达奇区',
+ 232721: '呼玛县',
+ 232722: '塔河县',
+ 232723: '漠河县'
+ },
+ 310000: {
+ 310100: '上海市',
+ },
+ 310100: {
+ 310101: '黄浦区',
+ 310104: '徐汇区',
+ 310105: '长宁区',
+ 310106: '静安区',
+ 310107: '普陀区',
+ 310108: '闸北区',
+ 310109: '虹口区',
+ 310110: '杨浦区',
+ 310112: '闵行区',
+ 310113: '宝山区',
+ 310114: '嘉定区',
+ 310115: '浦东新区',
+ 310116: '金山区',
+ 310117: '松江区',
+ 310118: '青浦区',
+ 310120: '奉贤区',
+ 310230: '崇明县'
+ },
+ 320000: {
+ 320100: '南京市',
+ 320200: '无锡市',
+ 320300: '徐州市',
+ 320400: '常州市',
+ 320500: '苏州市',
+ 320600: '南通市',
+ 320700: '连云港市',
+ 320800: '淮安市',
+ 320900: '盐城市',
+ 321000: '扬州市',
+ 321100: '镇江市',
+ 321200: '泰州市',
+ 321300: '宿迁市'
+ },
+ 320100: {
+ 320102: '玄武区',
+ 320104: '秦淮区',
+ 320105: '建邺区',
+ 320106: '鼓楼区',
+ 320111: '浦口区',
+ 320113: '栖霞区',
+ 320114: '雨花台区',
+ 320115: '江宁区',
+ 320116: '六合区',
+ 320117: '溧水区',
+ 320118: '高淳区'
+ },
+ 320200: {
+ 320202: '崇安区',
+ 320203: '南长区',
+ 320204: '北塘区',
+ 320205: '锡山区',
+ 320206: '惠山区',
+ 320211: '滨湖区',
+ 320281: '江阴市',
+ 320282: '宜兴市'
+ },
+ 320300: {
+ 320302: '鼓楼区',
+ 320303: '云龙区',
+ 320305: '贾汪区',
+ 320311: '泉山区',
+ 320312: '铜山区',
+ 320321: '丰县',
+ 320322: '沛县',
+ 320324: '睢宁县',
+ 320381: '新沂市',
+ 320382: '邳州市'
+ },
+ 320400: {
+ 320402: '天宁区',
+ 320404: '钟楼区',
+ 320411: '新北区',
+ 320412: '武进区',
+ 320481: '溧阳市',
+ 320482: '金坛区'
+ },
+ 320500: {
+ 320505: '虎丘区',
+ 320506: '吴中区',
+ 320507: '相城区',
+ 320508: '姑苏区',
+ 320509: '吴江区',
+ 320581: '常熟市',
+ 320582: '张家港市',
+ 320583: '昆山市',
+ 320585: '太仓市'
+ },
+ 320600: {
+ 320602: '崇川区',
+ 320611: '港闸区',
+ 320612: '通州区',
+ 320621: '海安县',
+ 320623: '如东县',
+ 320681: '启东市',
+ 320682: '如皋市',
+ 320684: '海门市'
+ },
+ 320700: {
+ 320703: '连云区',
+ 320706: '海州区',
+ 320707: '赣榆区',
+ 320722: '东海县',
+ 320723: '灌云县',
+ 320724: '灌南县'
+ },
+ 320800: {
+ 320802: '清河区',
+ 320803: '淮安区',
+ 320804: '淮阴区',
+ 320811: '清浦区',
+ 320826: '涟水县',
+ 320829: '洪泽县',
+ 320830: '盱眙县',
+ 320831: '金湖县'
+ },
+ 320900: {
+ 320902: '亭湖区',
+ 320903: '盐都区',
+ 320921: '响水县',
+ 320922: '滨海县',
+ 320923: '阜宁县',
+ 320924: '射阳县',
+ 320925: '建湖县',
+ 320981: '东台市',
+ 320982: '大丰市'
+ },
+ 321000: {
+ 321002: '广陵区',
+ 321003: '邗江区',
+ 321012: '江都区',
+ 321023: '宝应县',
+ 321081: '仪征市',
+ 321084: '高邮市'
+ },
+ 321100: {
+ 321102: '京口区',
+ 321111: '润州区',
+ 321112: '丹徒区',
+ 321181: '丹阳市',
+ 321182: '扬中市',
+ 321183: '句容市'
+ },
+ 321200: {
+ 321202: '海陵区',
+ 321203: '高港区',
+ 321204: '姜堰区',
+ 321281: '兴化市',
+ 321282: '靖江市',
+ 321283: '泰兴市'
+ },
+ 321300: {
+ 321302: '宿城区',
+ 321311: '宿豫区',
+ 321322: '沭阳县',
+ 321323: '泗阳县',
+ 321324: '泗洪县'
+ },
+ 330000: {
+ 330100: '杭州市',
+ 330200: '宁波市',
+ 330300: '温州市',
+ 330400: '嘉兴市',
+ 330500: '湖州市',
+ 330600: '绍兴市',
+ 330700: '金华市',
+ 330800: '衢州市',
+ 330900: '舟山市',
+ 331000: '台州市',
+ 331100: '丽水市'
+ },
+ 330100: {
+ 330102: '上城区',
+ 330103: '下城区',
+ 330104: '江干区',
+ 330105: '拱墅区',
+ 330106: '西湖区',
+ 330108: '滨江区',
+ 330109: '萧山区',
+ 330110: '余杭区',
+ 330111: '富阳区',
+ 330122: '桐庐县',
+ 330127: '淳安县',
+ 330182: '建德市',
+ 330185: '临安市'
+ },
+ 330200: {
+ 330203: '海曙区',
+ 330204: '江东区',
+ 330205: '江北区',
+ 330206: '北仑区',
+ 330211: '镇海区',
+ 330212: '鄞州区',
+ 330225: '象山县',
+ 330226: '宁海县',
+ 330281: '余姚市',
+ 330282: '慈溪市',
+ 330283: '奉化市'
+ },
+ 330300: {
+ 330302: '鹿城区',
+ 330303: '龙湾区',
+ 330304: '瓯海区',
+ 330322: '洞头县',
+ 330324: '永嘉县',
+ 330326: '平阳县',
+ 330327: '苍南县',
+ 330328: '文成县',
+ 330329: '泰顺县',
+ 330381: '瑞安市',
+ 330382: '乐清市'
+ },
+ 330400: {
+ 330402: '南湖区',
+ 330411: '秀洲区',
+ 330421: '嘉善县',
+ 330424: '海盐县',
+ 330481: '海宁市',
+ 330482: '平湖市',
+ 330483: '桐乡市'
+ },
+ 330500: {
+ 330502: '吴兴区',
+ 330503: '南浔区',
+ 330521: '德清县',
+ 330522: '长兴县',
+ 330523: '安吉县'
+ },
+ 330600: {
+ 330602: '越城区',
+ 330603: '柯桥区',
+ 330604: '上虞区',
+ 330624: '新昌县',
+ 330681: '诸暨市',
+ 330683: '嵊州市'
+ },
+ 330700: {
+ 330702: '婺城区',
+ 330703: '金东区',
+ 330723: '武义县',
+ 330726: '浦江县',
+ 330727: '磐安县',
+ 330781: '兰溪市',
+ 330782: '义乌市',
+ 330783: '东阳市',
+ 330784: '永康市'
+ },
+ 330800: {
+ 330802: '柯城区',
+ 330803: '衢江区',
+ 330822: '常山县',
+ 330824: '开化县',
+ 330825: '龙游县',
+ 330881: '江山市'
+ },
+ 330900: {
+ 330902: '定海区',
+ 330903: '普陀区',
+ 330921: '岱山县',
+ 330922: '嵊泗县'
+ },
+ 331000: {
+ 331002: '椒江区',
+ 331003: '黄岩区',
+ 331004: '路桥区',
+ 331021: '玉环县',
+ 331022: '三门县',
+ 331023: '天台县',
+ 331024: '仙居县',
+ 331081: '温岭市',
+ 331082: '临海市'
+ },
+ 331100: {
+ 331102: '莲都区',
+ 331121: '青田县',
+ 331122: '缙云县',
+ 331123: '遂昌县',
+ 331124: '松阳县',
+ 331125: '云和县',
+ 331126: '庆元县',
+ 331127: '景宁畲族自治县',
+ 331181: '龙泉市'
+ },
+ 340000: {
+ 340100: '合肥市',
+ 340200: '芜湖市',
+ 340300: '蚌埠市',
+ 340400: '淮南市',
+ 340500: '马鞍山市',
+ 340600: '淮北市',
+ 340700: '铜陵市',
+ 340800: '安庆市',
+ 341000: '黄山市',
+ 341100: '滁州市',
+ 341200: '阜阳市',
+ 341300: '宿州市',
+ 341500: '六安市',
+ 341600: '亳州市',
+ 341700: '池州市',
+ 341800: '宣城市'
+ },
+ 340100: {
+ 340102: '瑶海区',
+ 340103: '庐阳区',
+ 340104: '蜀山区',
+ 340111: '包河区',
+ 340121: '长丰县',
+ 340122: '肥东县',
+ 340123: '肥西县',
+ 340124: '庐江县',
+ 340181: '巢湖市'
+ },
+ 340200: {
+ 340202: '镜湖区',
+ 340203: '弋江区',
+ 340207: '鸠江区',
+ 340208: '三山区',
+ 340221: '芜湖县',
+ 340222: '繁昌县',
+ 340223: '南陵县',
+ 340225: '无为县'
+ },
+ 340300: {
+ 340302: '龙子湖区',
+ 340303: '蚌山区',
+ 340304: '禹会区',
+ 340311: '淮上区',
+ 340321: '怀远县',
+ 340322: '五河县',
+ 340323: '固镇县'
+ },
+ 340400: {
+ 340402: '大通区',
+ 340403: '田家庵区',
+ 340404: '谢家集区',
+ 340405: '八公山区',
+ 340406: '潘集区',
+ 340421: '凤台县'
+ },
+ 340500: {
+ 340503: '花山区',
+ 340504: '雨山区',
+ 340506: '博望区',
+ 340521: '当涂县',
+ 340522: '含山县',
+ 340523: '和县'
+ },
+ 340600: {
+ 340602: '杜集区',
+ 340603: '相山区',
+ 340604: '烈山区',
+ 340621: '濉溪县'
+ },
+ 340700: {
+ 340702: '铜官山区',
+ 340703: '狮子山区',
+ 340711: '郊区',
+ 340721: '铜陵县'
+ },
+ 340800: {
+ 340802: '迎江区',
+ 340803: '大观区',
+ 340811: '宜秀区',
+ 340822: '怀宁县',
+ 340823: '枞阳县',
+ 340824: '潜山县',
+ 340825: '太湖县',
+ 340826: '宿松县',
+ 340827: '望江县',
+ 340828: '岳西县',
+ 340881: '桐城市'
+ },
+ 341000: {
+ 341002: '屯溪区',
+ 341003: '黄山区',
+ 341004: '徽州区',
+ 341021: '歙县',
+ 341022: '休宁县',
+ 341023: '黟县',
+ 341024: '祁门县'
+ },
+ 341100: {
+ 341102: '琅琊区',
+ 341103: '南谯区',
+ 341122: '来安县',
+ 341124: '全椒县',
+ 341125: '定远县',
+ 341126: '凤阳县',
+ 341181: '天长市',
+ 341182: '明光市'
+ },
+ 341200: {
+ 341202: '颍州区',
+ 341203: '颍东区',
+ 341204: '颍泉区',
+ 341221: '临泉县',
+ 341222: '太和县',
+ 341225: '阜南县',
+ 341226: '颍上县',
+ 341282: '界首市'
+ },
+ 341300: {
+ 341302: '埇桥区',
+ 341321: '砀山县',
+ 341322: '萧县',
+ 341323: '灵璧县',
+ 341324: '泗县'
+ },
+ 341500: {
+ 341502: '金安区',
+ 341503: '裕安区',
+ 341521: '寿县',
+ 341522: '霍邱县',
+ 341523: '舒城县',
+ 341524: '金寨县',
+ 341525: '霍山县'
+ },
+ 341600: {
+ 341602: '谯城区',
+ 341621: '涡阳县',
+ 341622: '蒙城县',
+ 341623: '利辛县'
+ },
+ 341700: {
+ 341702: '贵池区',
+ 341721: '东至县',
+ 341722: '石台县',
+ 341723: '青阳县'
+ },
+ 341800: {
+ 341802: '宣州区',
+ 341821: '郎溪县',
+ 341822: '广德县',
+ 341823: '泾县',
+ 341824: '绩溪县',
+ 341825: '旌德县',
+ 341881: '宁国市'
+ },
+ 350000: {
+ 350100: '福州市',
+ 350200: '厦门市',
+ 350300: '莆田市',
+ 350400: '三明市',
+ 350500: '泉州市',
+ 350600: '漳州市',
+ 350700: '南平市',
+ 350800: '龙岩市',
+ 350900: '宁德市'
+ },
+ 350100: {
+ 350102: '鼓楼区',
+ 350103: '台江区',
+ 350104: '仓山区',
+ 350105: '马尾区',
+ 350111: '晋安区',
+ 350121: '闽侯县',
+ 350122: '连江县',
+ 350123: '罗源县',
+ 350124: '闽清县',
+ 350125: '永泰县',
+ 350128: '平潭县',
+ 350181: '福清市',
+ 350182: '长乐市'
+ },
+ 350200: {
+ 350203: '思明区',
+ 350205: '海沧区',
+ 350206: '湖里区',
+ 350211: '集美区',
+ 350212: '同安区',
+ 350213: '翔安区'
+ },
+ 350300: {
+ 350302: '城厢区',
+ 350303: '涵江区',
+ 350304: '荔城区',
+ 350305: '秀屿区',
+ 350322: '仙游县'
+ },
+ 350400: {
+ 350402: '梅列区',
+ 350403: '三元区',
+ 350421: '明溪县',
+ 350423: '清流县',
+ 350424: '宁化县',
+ 350425: '大田县',
+ 350426: '尤溪县',
+ 350427: '沙县',
+ 350428: '将乐县',
+ 350429: '泰宁县',
+ 350430: '建宁县',
+ 350481: '永安市'
+ },
+ 350500: {
+ 350502: '鲤城区',
+ 350503: '丰泽区',
+ 350504: '洛江区',
+ 350505: '泉港区',
+ 350521: '惠安县',
+ 350524: '安溪县',
+ 350525: '永春县',
+ 350526: '德化县',
+ 350527: '金门县',
+ 350581: '石狮市',
+ 350582: '晋江市',
+ 350583: '南安市'
+ },
+ 350600: {
+ 350602: '芗城区',
+ 350603: '龙文区',
+ 350622: '云霄县',
+ 350623: '漳浦县',
+ 350624: '诏安县',
+ 350625: '长泰县',
+ 350626: '东山县',
+ 350627: '南靖县',
+ 350628: '平和县',
+ 350629: '华安县',
+ 350681: '龙海市'
+ },
+ 350700: {
+ 350702: '延平区',
+ 350703: '建阳区',
+ 350721: '顺昌县',
+ 350722: '浦城县',
+ 350723: '光泽县',
+ 350724: '松溪县',
+ 350725: '政和县',
+ 350781: '邵武市',
+ 350782: '武夷山市',
+ 350783: '建瓯市'
+ },
+ 350800: {
+ 350802: '新罗区',
+ 350803: '永定区',
+ 350821: '长汀县',
+ 350823: '上杭县',
+ 350824: '武平县',
+ 350825: '连城县',
+ 350881: '漳平市'
+ },
+ 350900: {
+ 350902: '蕉城区',
+ 350921: '霞浦县',
+ 350922: '古田县',
+ 350923: '屏南县',
+ 350924: '寿宁县',
+ 350925: '周宁县',
+ 350926: '柘荣县',
+ 350981: '福安市',
+ 350982: '福鼎市'
+ },
+ 360000: {
+ 360100: '南昌市',
+ 360200: '景德镇市',
+ 360300: '萍乡市',
+ 360400: '九江市',
+ 360500: '新余市',
+ 360600: '鹰潭市',
+ 360700: '赣州市',
+ 360800: '吉安市',
+ 360900: '宜春市',
+ 361000: '抚州市',
+ 361100: '上饶市'
+ },
+ 360100: {
+ 360102: '东湖区',
+ 360103: '西湖区',
+ 360104: '青云谱区',
+ 360105: '湾里区',
+ 360111: '青山湖区',
+ 360121: '南昌县',
+ 360122: '新建县',
+ 360123: '安义县',
+ 360124: '进贤县'
+ },
+ 360200: {
+ 360202: '昌江区',
+ 360203: '珠山区',
+ 360222: '浮梁县',
+ 360281: '乐平市'
+ },
+ 360300: {
+ 360302: '安源区',
+ 360313: '湘东区',
+ 360321: '莲花县',
+ 360322: '上栗县',
+ 360323: '芦溪县'
+ },
+ 360400: {
+ 360402: '庐山区',
+ 360403: '浔阳区',
+ 360421: '九江县',
+ 360423: '武宁县',
+ 360424: '修水县',
+ 360425: '永修县',
+ 360426: '德安县',
+ 360427: '星子县',
+ 360428: '都昌县',
+ 360429: '湖口县',
+ 360430: '彭泽县',
+ 360481: '瑞昌市',
+ 360482: '共青城市'
+ },
+ 360500: {
+ 360502: '渝水区',
+ 360521: '分宜县'
+ },
+ 360600: {
+ 360602: '月湖区',
+ 360622: '余江县',
+ 360681: '贵溪市'
+ },
+ 360700: {
+ 360702: '章贡区',
+ 360703: '南康区',
+ 360721: '赣县',
+ 360722: '信丰县',
+ 360723: '大余县',
+ 360724: '上犹县',
+ 360725: '崇义县',
+ 360726: '安远县',
+ 360727: '龙南县',
+ 360728: '定南县',
+ 360729: '全南县',
+ 360730: '宁都县',
+ 360731: '于都县',
+ 360732: '兴国县',
+ 360733: '会昌县',
+ 360734: '寻乌县',
+ 360735: '石城县',
+ 360781: '瑞金市'
+ },
+ 360800: {
+ 360802: '吉州区',
+ 360803: '青原区',
+ 360821: '吉安县',
+ 360822: '吉水县',
+ 360823: '峡江县',
+ 360824: '新干县',
+ 360825: '永丰县',
+ 360826: '泰和县',
+ 360827: '遂川县',
+ 360828: '万安县',
+ 360829: '安福县',
+ 360830: '永新县',
+ 360881: '井冈山市'
+ },
+ 360900: {
+ 360902: '袁州区',
+ 360921: '奉新县',
+ 360922: '万载县',
+ 360923: '上高县',
+ 360924: '宜丰县',
+ 360925: '靖安县',
+ 360926: '铜鼓县',
+ 360981: '丰城市',
+ 360982: '樟树市',
+ 360983: '高安市'
+ },
+ 361000: {
+ 361002: '临川区',
+ 361021: '南城县',
+ 361022: '黎川县',
+ 361023: '南丰县',
+ 361024: '崇仁县',
+ 361025: '乐安县',
+ 361026: '宜黄县',
+ 361027: '金溪县',
+ 361028: '资溪县',
+ 361029: '东乡县',
+ 361030: '广昌县'
+ },
+ 361100: {
+ 361102: '信州区',
+ 361103: '广丰区',
+ 361121: '上饶县',
+ 361123: '玉山县',
+ 361124: '铅山县',
+ 361125: '横峰县',
+ 361126: '弋阳县',
+ 361127: '余干县',
+ 361128: '鄱阳县',
+ 361129: '万年县',
+ 361130: '婺源县',
+ 361181: '德兴市'
+ },
+ 370000: {
+ 370100: '济南市',
+ 370200: '青岛市',
+ 370300: '淄博市',
+ 370400: '枣庄市',
+ 370500: '东营市',
+ 370600: '烟台市',
+ 370700: '潍坊市',
+ 370800: '济宁市',
+ 370900: '泰安市',
+ 371000: '威海市',
+ 371100: '日照市',
+ 371200: '莱芜市',
+ 371300: '临沂市',
+ 371400: '德州市',
+ 371500: '聊城市',
+ 371600: '滨州市',
+ 371700: '菏泽市'
+ },
+ 370100: {
+ 370102: '历下区',
+ 370103: '市中区',
+ 370104: '槐荫区',
+ 370105: '天桥区',
+ 370112: '历城区',
+ 370113: '长清区',
+ 370124: '平阴县',
+ 370125: '济阳县',
+ 370126: '商河县',
+ 370181: '章丘市'
+ },
+ 370200: {
+ 370202: '市南区',
+ 370203: '市北区',
+ 370211: '黄岛区',
+ 370212: '崂山区',
+ 370213: '李沧区',
+ 370214: '城阳区',
+ 370281: '胶州市',
+ 370282: '即墨市',
+ 370283: '平度市',
+ 370285: '莱西市'
+ },
+ 370300: {
+ 370302: '淄川区',
+ 370303: '张店区',
+ 370304: '博山区',
+ 370305: '临淄区',
+ 370306: '周村区',
+ 370321: '桓台县',
+ 370322: '高青县',
+ 370323: '沂源县'
+ },
+ 370400: {
+ 370402: '市中区',
+ 370403: '薛城区',
+ 370404: '峄城区',
+ 370405: '台儿庄区',
+ 370406: '山亭区',
+ 370481: '滕州市'
+ },
+ 370500: {
+ 370502: '东营区',
+ 370503: '河口区',
+ 370521: '垦利县',
+ 370522: '利津县',
+ 370523: '广饶县'
+ },
+ 370600: {
+ 370602: '芝罘区',
+ 370611: '福山区',
+ 370612: '牟平区',
+ 370613: '莱山区',
+ 370634: '长岛县',
+ 370681: '龙口市',
+ 370682: '莱阳市',
+ 370683: '莱州市',
+ 370684: '蓬莱市',
+ 370685: '招远市',
+ 370686: '栖霞市',
+ 370687: '海阳市'
+ },
+ 370700: {
+ 370702: '潍城区',
+ 370703: '寒亭区',
+ 370704: '坊子区',
+ 370705: '奎文区',
+ 370724: '临朐县',
+ 370725: '昌乐县',
+ 370781: '青州市',
+ 370782: '诸城市',
+ 370783: '寿光市',
+ 370784: '安丘市',
+ 370785: '高密市',
+ 370786: '昌邑市'
+ },
+ 370800: {
+ 370811: '任城区',
+ 370812: '兖州区',
+ 370826: '微山县',
+ 370827: '鱼台县',
+ 370828: '金乡县',
+ 370829: '嘉祥县',
+ 370830: '汶上县',
+ 370831: '泗水县',
+ 370832: '梁山县',
+ 370881: '曲阜市',
+ 370883: '邹城市'
+ },
+ 370900: {
+ 370902: '泰山区',
+ 370911: '岱岳区',
+ 370921: '宁阳县',
+ 370923: '东平县',
+ 370982: '新泰市',
+ 370983: '肥城市'
+ },
+ 371000: {
+ 371002: '环翠区',
+ 371003: '文登区',
+ 371082: '荣成市',
+ 371083: '乳山市'
+ },
+ 371100: {
+ 371102: '东港区',
+ 371103: '岚山区',
+ 371121: '五莲县',
+ 371122: '莒县'
+ },
+ 371200: {
+ 371202: '莱城区',
+ 371203: '钢城区'
+ },
+ 371300: {
+ 371302: '兰山区',
+ 371311: '罗庄区',
+ 371312: '河东区',
+ 371321: '沂南县',
+ 371322: '郯城县',
+ 371323: '沂水县',
+ 371324: '兰陵县',
+ 371325: '费县',
+ 371326: '平邑县',
+ 371327: '莒南县',
+ 371328: '蒙阴县',
+ 371329: '临沭县'
+ },
+ 371400: {
+ 371402: '德城区',
+ 371403: '陵城区',
+ 371422: '宁津县',
+ 371423: '庆云县',
+ 371424: '临邑县',
+ 371425: '齐河县',
+ 371426: '平原县',
+ 371427: '夏津县',
+ 371428: '武城县',
+ 371481: '乐陵市',
+ 371482: '禹城市'
+ },
+ 371500: {
+ 371502: '东昌府区',
+ 371521: '阳谷县',
+ 371522: '莘县',
+ 371523: '茌平县',
+ 371524: '东阿县',
+ 371525: '冠县',
+ 371526: '高唐县',
+ 371581: '临清市'
+ },
+ 371600: {
+ 371602: '滨城区',
+ 371603: '沾化区',
+ 371621: '惠民县',
+ 371622: '阳信县',
+ 371623: '无棣县',
+ 371625: '博兴县',
+ 371626: '邹平县'
+ },
+ 371700: {
+ 371702: '牡丹区',
+ 371721: '曹县',
+ 371722: '单县',
+ 371723: '成武县',
+ 371724: '巨野县',
+ 371725: '郓城县',
+ 371726: '鄄城县',
+ 371727: '定陶县',
+ 371728: '东明县'
+ },
+ 410000: {
+ 410100: '郑州市',
+ 410200: '开封市',
+ 410300: '洛阳市',
+ 410400: '平顶山市',
+ 410500: '安阳市',
+ 410600: '鹤壁市',
+ 410700: '新乡市',
+ 410800: '焦作市',
+ 410900: '濮阳市',
+ 411000: '许昌市',
+ 411100: '漯河市',
+ 411200: '三门峡市',
+ 411300: '南阳市',
+ 411400: '商丘市',
+ 411500: '信阳市',
+ 411600: '周口市',
+ 411700: '驻马店市',
+ 419001: '济源市'
+ },
+ 410100: {
+ 410102: '中原区',
+ 410103: '二七区',
+ 410104: '管城回族区',
+ 410105: '金水区',
+ 410106: '上街区',
+ 410108: '惠济区',
+ 410122: '中牟县',
+ 410181: '巩义市',
+ 410182: '荥阳市',
+ 410183: '新密市',
+ 410184: '新郑市',
+ 410185: '登封市'
+ },
+ 410200: {
+ 410202: '龙亭区',
+ 410203: '顺河回族区',
+ 410204: '鼓楼区',
+ 410205: '禹王台区',
+ 410212: '祥符区',
+ 410221: '杞县',
+ 410222: '通许县',
+ 410223: '尉氏县',
+ 410225: '兰考县'
+ },
+ 410300: {
+ 410302: '老城区',
+ 410303: '西工区',
+ 410304: '瀍河回族区',
+ 410305: '涧西区',
+ 410306: '吉利区',
+ 410311: '洛龙区',
+ 410322: '孟津县',
+ 410323: '新安县',
+ 410324: '栾川县',
+ 410325: '嵩县',
+ 410326: '汝阳县',
+ 410327: '宜阳县',
+ 410328: '洛宁县',
+ 410329: '伊川县',
+ 410381: '偃师市'
+ },
+ 410400: {
+ 410402: '新华区',
+ 410403: '卫东区',
+ 410404: '石龙区',
+ 410411: '湛河区',
+ 410421: '宝丰县',
+ 410422: '叶县',
+ 410423: '鲁山县',
+ 410425: '郏县',
+ 410481: '舞钢市',
+ 410482: '汝州市'
+ },
+ 410500: {
+ 410502: '文峰区',
+ 410503: '北关区',
+ 410505: '殷都区',
+ 410506: '龙安区',
+ 410522: '安阳县',
+ 410523: '汤阴县',
+ 410526: '滑县',
+ 410527: '内黄县',
+ 410581: '林州市'
+ },
+ 410600: {
+ 410602: '鹤山区',
+ 410603: '山城区',
+ 410611: '淇滨区',
+ 410621: '浚县',
+ 410622: '淇县'
+ },
+ 410700: {
+ 410702: '红旗区',
+ 410703: '卫滨区',
+ 410704: '凤泉区',
+ 410711: '牧野区',
+ 410721: '新乡县',
+ 410724: '获嘉县',
+ 410725: '原阳县',
+ 410726: '延津县',
+ 410727: '封丘县',
+ 410728: '长垣县',
+ 410781: '卫辉市',
+ 410782: '辉县市'
+ },
+ 410800: {
+ 410802: '解放区',
+ 410803: '中站区',
+ 410804: '马村区',
+ 410811: '山阳区',
+ 410821: '修武县',
+ 410822: '博爱县',
+ 410823: '武陟县',
+ 410825: '温县',
+ 410882: '沁阳市',
+ 410883: '孟州市'
+ },
+ 410900: {
+ 410902: '华龙区',
+ 410922: '清丰县',
+ 410923: '南乐县',
+ 410926: '范县',
+ 410927: '台前县',
+ 410928: '濮阳县'
+ },
+ 411000: {
+ 411002: '魏都区',
+ 411023: '许昌县',
+ 411024: '鄢陵县',
+ 411025: '襄城县',
+ 411081: '禹州市',
+ 411082: '长葛市'
+ },
+ 411100: {
+ 411102: '源汇区',
+ 411103: '郾城区',
+ 411104: '召陵区',
+ 411121: '舞阳县',
+ 411122: '临颍县'
+ },
+ 411200: {
+ 411202: '湖滨区',
+ 411203: '陕州区',
+ 411221: '渑池县',
+ 411224: '卢氏县',
+ 411281: '义马市',
+ 411282: '灵宝市'
+ },
+ 411300: {
+ 411302: '宛城区',
+ 411303: '卧龙区',
+ 411321: '南召县',
+ 411322: '方城县',
+ 411323: '西峡县',
+ 411324: '镇平县',
+ 411325: '内乡县',
+ 411326: '淅川县',
+ 411327: '社旗县',
+ 411328: '唐河县',
+ 411329: '新野县',
+ 411330: '桐柏县',
+ 411381: '邓州市'
+ },
+ 411400: {
+ 411402: '梁园区',
+ 411403: '睢阳区',
+ 411421: '民权县',
+ 411422: '睢县',
+ 411423: '宁陵县',
+ 411424: '柘城县',
+ 411425: '虞城县',
+ 411426: '夏邑县',
+ 411481: '永城市'
+ },
+ 411500: {
+ 411502: '浉河区',
+ 411503: '平桥区',
+ 411521: '罗山县',
+ 411522: '光山县',
+ 411523: '新县',
+ 411524: '商城县',
+ 411525: '固始县',
+ 411526: '潢川县',
+ 411527: '淮滨县',
+ 411528: '息县'
+ },
+ 411600: {
+ 411602: '川汇区',
+ 411621: '扶沟县',
+ 411622: '西华县',
+ 411623: '商水县',
+ 411624: '沈丘县',
+ 411625: '郸城县',
+ 411626: '淮阳县',
+ 411627: '太康县',
+ 411628: '鹿邑县',
+ 411681: '项城市'
+ },
+ 411700: {
+ 411702: '驿城区',
+ 411721: '西平县',
+ 411722: '上蔡县',
+ 411723: '平舆县',
+ 411724: '正阳县',
+ 411725: '确山县',
+ 411726: '泌阳县',
+ 411727: '汝南县',
+ 411728: '遂平县',
+ 411729: '新蔡县'
+ },
+ 420000: {
+ 420100: '武汉市',
+ 420200: '黄石市',
+ 420300: '十堰市',
+ 420500: '宜昌市',
+ 420600: '襄阳市',
+ 420700: '鄂州市',
+ 420800: '荆门市',
+ 420900: '孝感市',
+ 421000: '荆州市',
+ 421100: '黄冈市',
+ 421200: '咸宁市',
+ 421300: '随州市',
+ 422800: '恩施土家族苗族自治州',
+ 429004: '仙桃市',
+ 429005: '潜江市',
+ 429006: '天门市',
+ 429021: '神农架林区'
+ },
+ 420100: {
+ 420102: '江岸区',
+ 420103: '江汉区',
+ 420104: '硚口区',
+ 420105: '汉阳区',
+ 420106: '武昌区',
+ 420107: '青山区',
+ 420111: '洪山区',
+ 420112: '东西湖区',
+ 420113: '汉南区',
+ 420114: '蔡甸区',
+ 420115: '江夏区',
+ 420116: '黄陂区',
+ 420117: '新洲区'
+ },
+ 420200: {
+ 420202: '黄石港区',
+ 420203: '西塞山区',
+ 420204: '下陆区',
+ 420205: '铁山区',
+ 420222: '阳新县',
+ 420281: '大冶市'
+ },
+ 420300: {
+ 420302: '茅箭区',
+ 420303: '张湾区',
+ 420304: '郧阳区',
+ 420322: '郧西县',
+ 420323: '竹山县',
+ 420324: '竹溪县',
+ 420325: '房县',
+ 420381: '丹江口市'
+ },
+ 420500: {
+ 420502: '西陵区',
+ 420503: '伍家岗区',
+ 420504: '点军区',
+ 420505: '猇亭区',
+ 420506: '夷陵区',
+ 420525: '远安县',
+ 420526: '兴山县',
+ 420527: '秭归县',
+ 420528: '长阳土家族自治县',
+ 420529: '五峰土家族自治县',
+ 420581: '宜都市',
+ 420582: '当阳市',
+ 420583: '枝江市'
+ },
+ 420600: {
+ 420602: '襄城区',
+ 420606: '樊城区',
+ 420607: '襄州区',
+ 420624: '南漳县',
+ 420625: '谷城县',
+ 420626: '保康县',
+ 420682: '老河口市',
+ 420683: '枣阳市',
+ 420684: '宜城市'
+ },
+ 420700: {
+ 420702: '梁子湖区',
+ 420703: '华容区',
+ 420704: '鄂城区'
+ },
+ 420800: {
+ 420802: '东宝区',
+ 420804: '掇刀区',
+ 420821: '京山县',
+ 420822: '沙洋县',
+ 420881: '钟祥市'
+ },
+ 420900: {
+ 420902: '孝南区',
+ 420921: '孝昌县',
+ 420922: '大悟县',
+ 420923: '云梦县',
+ 420981: '应城市',
+ 420982: '安陆市',
+ 420984: '汉川市'
+ },
+ 421000: {
+ 421002: '沙市区',
+ 421003: '荆州区',
+ 421022: '公安县',
+ 421023: '监利县',
+ 421024: '江陵县',
+ 421081: '石首市',
+ 421083: '洪湖市',
+ 421087: '松滋市'
+ },
+ 421100: {
+ 421102: '黄州区',
+ 421121: '团风县',
+ 421122: '红安县',
+ 421123: '罗田县',
+ 421124: '英山县',
+ 421125: '浠水县',
+ 421126: '蕲春县',
+ 421127: '黄梅县',
+ 421181: '麻城市',
+ 421182: '武穴市'
+ },
+ 421200: {
+ 421202: '咸安区',
+ 421221: '嘉鱼县',
+ 421222: '通城县',
+ 421223: '崇阳县',
+ 421224: '通山县',
+ 421281: '赤壁市'
+ },
+ 421300: {
+ 421303: '曾都区',
+ 421321: '随县',
+ 421381: '广水市'
+ },
+ 422800: {
+ 422801: '恩施市',
+ 422802: '利川市',
+ 422822: '建始县',
+ 422823: '巴东县',
+ 422825: '宣恩县',
+ 422826: '咸丰县',
+ 422827: '来凤县',
+ 422828: '鹤峰县'
+ },
+ 430000: {
+ 430100: '长沙市',
+ 430200: '株洲市',
+ 430300: '湘潭市',
+ 430400: '衡阳市',
+ 430500: '邵阳市',
+ 430600: '岳阳市',
+ 430700: '常德市',
+ 430800: '张家界市',
+ 430900: '益阳市',
+ 431000: '郴州市',
+ 431100: '永州市',
+ 431200: '怀化市',
+ 431300: '娄底市',
+ 433100: '湘西土家族苗族自治州'
+ },
+ 430100: {
+ 430102: '芙蓉区',
+ 430103: '天心区',
+ 430104: '岳麓区',
+ 430105: '开福区',
+ 430111: '雨花区',
+ 430112: '望城区',
+ 430121: '长沙县',
+ 430124: '宁乡县',
+ 430181: '浏阳市'
+ },
+ 430200: {
+ 430202: '荷塘区',
+ 430203: '芦淞区',
+ 430204: '石峰区',
+ 430211: '天元区',
+ 430221: '株洲县',
+ 430223: '攸县',
+ 430224: '茶陵县',
+ 430225: '炎陵县',
+ 430281: '醴陵市'
+ },
+ 430300: {
+ 430302: '雨湖区',
+ 430304: '岳塘区',
+ 430321: '湘潭县',
+ 430381: '湘乡市',
+ 430382: '韶山市'
+ },
+ 430400: {
+ 430405: '珠晖区',
+ 430406: '雁峰区',
+ 430407: '石鼓区',
+ 430408: '蒸湘区',
+ 430412: '南岳区',
+ 430421: '衡阳县',
+ 430422: '衡南县',
+ 430423: '衡山县',
+ 430424: '衡东县',
+ 430426: '祁东县',
+ 430481: '耒阳市',
+ 430482: '常宁市'
+ },
+ 430500: {
+ 430502: '双清区',
+ 430503: '大祥区',
+ 430511: '北塔区',
+ 430521: '邵东县',
+ 430522: '新邵县',
+ 430523: '邵阳县',
+ 430524: '隆回县',
+ 430525: '洞口县',
+ 430527: '绥宁县',
+ 430528: '新宁县',
+ 430529: '城步苗族自治县',
+ 430581: '武冈市'
+ },
+ 430600: {
+ 430602: '岳阳楼区',
+ 430603: '云溪区',
+ 430611: '君山区',
+ 430621: '岳阳县',
+ 430623: '华容县',
+ 430624: '湘阴县',
+ 430626: '平江县',
+ 430681: '汨罗市',
+ 430682: '临湘市'
+ },
+ 430700: {
+ 430702: '武陵区',
+ 430703: '鼎城区',
+ 430721: '安乡县',
+ 430722: '汉寿县',
+ 430723: '澧县',
+ 430724: '临澧县',
+ 430725: '桃源县',
+ 430726: '石门县',
+ 430781: '津市市'
+ },
+ 430800: {
+ 430802: '永定区',
+ 430811: '武陵源区',
+ 430821: '慈利县',
+ 430822: '桑植县'
+ },
+ 430900: {
+ 430902: '资阳区',
+ 430903: '赫山区',
+ 430921: '南县',
+ 430922: '桃江县',
+ 430923: '安化县',
+ 430981: '沅江市'
+ },
+ 431000: {
+ 431002: '北湖区',
+ 431003: '苏仙区',
+ 431021: '桂阳县',
+ 431022: '宜章县',
+ 431023: '永兴县',
+ 431024: '嘉禾县',
+ 431025: '临武县',
+ 431026: '汝城县',
+ 431027: '桂东县',
+ 431028: '安仁县',
+ 431081: '资兴市'
+ },
+ 431100: {
+ 431102: '零陵区',
+ 431103: '冷水滩区',
+ 431121: '祁阳县',
+ 431122: '东安县',
+ 431123: '双牌县',
+ 431124: '道县',
+ 431125: '江永县',
+ 431126: '宁远县',
+ 431127: '蓝山县',
+ 431128: '新田县',
+ 431129: '江华瑶族自治县'
+ },
+ 431200: {
+ 431202: '鹤城区',
+ 431221: '中方县',
+ 431222: '沅陵县',
+ 431223: '辰溪县',
+ 431224: '溆浦县',
+ 431225: '会同县',
+ 431226: '麻阳苗族自治县',
+ 431227: '新晃侗族自治县',
+ 431228: '芷江侗族自治县',
+ 431229: '靖州苗族侗族自治县',
+ 431230: '通道侗族自治县',
+ 431281: '洪江市'
+ },
+ 431300: {
+ 431302: '娄星区',
+ 431321: '双峰县',
+ 431322: '新化县',
+ 431381: '冷水江市',
+ 431382: '涟源市'
+ },
+ 433100: {
+ 433101: '吉首市',
+ 433122: '泸溪县',
+ 433123: '凤凰县',
+ 433124: '花垣县',
+ 433125: '保靖县',
+ 433126: '古丈县',
+ 433127: '永顺县',
+ 433130: '龙山县'
+ },
+ 440000: {
+ 440100: '广州市',
+ 440200: '韶关市',
+ 440300: '深圳市',
+ 440400: '珠海市',
+ 440500: '汕头市',
+ 440600: '佛山市',
+ 440700: '江门市',
+ 440800: '湛江市',
+ 440900: '茂名市',
+ 441200: '肇庆市',
+ 441300: '惠州市',
+ 441400: '梅州市',
+ 441500: '汕尾市',
+ 441600: '河源市',
+ 441700: '阳江市',
+ 441800: '清远市',
+ 441900: '东莞市',
+ 442000: '中山市',
+ 445100: '潮州市',
+ 445200: '揭阳市',
+ 445300: '云浮市'
+ },
+ 440100: {
+ 440103: '荔湾区',
+ 440104: '越秀区',
+ 440105: '海珠区',
+ 440106: '天河区',
+ 440111: '白云区',
+ 440112: '黄埔区',
+ 440113: '番禺区',
+ 440114: '花都区',
+ 440115: '南沙区',
+ 440117: '从化区',
+ 440118: '增城区'
+ },
+ 440200: {
+ 440203: '武江区',
+ 440204: '浈江区',
+ 440205: '曲江区',
+ 440222: '始兴县',
+ 440224: '仁化县',
+ 440229: '翁源县',
+ 440232: '乳源瑶族自治县',
+ 440233: '新丰县',
+ 440281: '乐昌市',
+ 440282: '南雄市'
+ },
+ 440300: {
+ 440303: '罗湖区',
+ 440304: '福田区',
+ 440305: '南山区',
+ 440306: '宝安区',
+ 440307: '龙岗区',
+ 440308: '盐田区'
+ },
+ 440400: {
+ 440402: '香洲区',
+ 440403: '斗门区',
+ 440404: '金湾区'
+ },
+ 440500: {
+ 440507: '龙湖区',
+ 440511: '金平区',
+ 440512: '濠江区',
+ 440513: '潮阳区',
+ 440514: '潮南区',
+ 440515: '澄海区',
+ 440523: '南澳县'
+ },
+ 440600: {
+ 440604: '禅城区',
+ 440605: '南海区',
+ 440606: '顺德区',
+ 440607: '三水区',
+ 440608: '高明区'
+ },
+ 440700: {
+ 440703: '蓬江区',
+ 440704: '江海区',
+ 440705: '新会区',
+ 440781: '台山市',
+ 440783: '开平市',
+ 440784: '鹤山市',
+ 440785: '恩平市'
+ },
+ 440800: {
+ 440802: '赤坎区',
+ 440803: '霞山区',
+ 440804: '坡头区',
+ 440811: '麻章区',
+ 440823: '遂溪县',
+ 440825: '徐闻县',
+ 440881: '廉江市',
+ 440882: '雷州市',
+ 440883: '吴川市'
+ },
+ 440900: {
+ 440902: '茂南区',
+ 440904: '电白区',
+ 440981: '高州市',
+ 440982: '化州市',
+ 440983: '信宜市'
+ },
+ 441200: {
+ 441202: '端州区',
+ 441203: '鼎湖区',
+ 441223: '广宁县',
+ 441224: '怀集县',
+ 441225: '封开县',
+ 441226: '德庆县',
+ 441283: '高要区',
+ 441284: '四会市'
+ },
+ 441300: {
+ 441302: '惠城区',
+ 441303: '惠阳区',
+ 441322: '博罗县',
+ 441323: '惠东县',
+ 441324: '龙门县'
+ },
+ 441400: {
+ 441402: '梅江区',
+ 441403: '梅县区',
+ 441422: '大埔县',
+ 441423: '丰顺县',
+ 441424: '五华县',
+ 441426: '平远县',
+ 441427: '蕉岭县',
+ 441481: '兴宁市'
+ },
+ 441500: {
+ 441502: '城区',
+ 441521: '海丰县',
+ 441523: '陆河县',
+ 441581: '陆丰市'
+ },
+ 441600: {
+ 441602: '源城区',
+ 441621: '紫金县',
+ 441622: '龙川县',
+ 441623: '连平县',
+ 441624: '和平县',
+ 441625: '东源县'
+ },
+ 441700: {
+ 441702: '江城区',
+ 441704: '阳东区',
+ 441721: '阳西县',
+ 441781: '阳春市'
+ },
+ 441800: {
+ 441802: '清城区',
+ 441803: '清新区',
+ 441821: '佛冈县',
+ 441823: '阳山县',
+ 441825: '连山壮族瑶族自治县',
+ 441826: '连南瑶族自治县',
+ 441881: '英德市',
+ 441882: '连州市'
+ },
+ 441900: {
+ 441900: '三元里'
+ },
+ 442000: {
+ 442000: '湖滨北路'
+ },
+ 445100: {
+ 445102: '湘桥区',
+ 445103: '潮安区',
+ 445122: '饶平县'
+ },
+ 445200: {
+ 445202: '榕城区',
+ 445203: '揭东区',
+ 445222: '揭西县',
+ 445224: '惠来县',
+ 445281: '普宁市'
+ },
+ 445300: {
+ 445302: '云城区',
+ 445303: '云安区',
+ 445321: '新兴县',
+ 445322: '郁南县',
+ 445381: '罗定市'
+ },
+ 450000: {
+ 450100: '南宁市',
+ 450200: '柳州市',
+ 450300: '桂林市',
+ 450400: '梧州市',
+ 450500: '北海市',
+ 450600: '防城港市',
+ 450700: '钦州市',
+ 450800: '贵港市',
+ 450900: '玉林市',
+ 451000: '百色市',
+ 451100: '贺州市',
+ 451200: '河池市',
+ 451300: '来宾市',
+ 451400: '崇左市'
+ },
+ 450100: {
+ 450102: '兴宁区',
+ 450103: '青秀区',
+ 450105: '江南区',
+ 450107: '西乡塘区',
+ 450108: '良庆区',
+ 450109: '邕宁区',
+ 450110: '武鸣区',
+ 450123: '隆安县',
+ 450124: '马山县',
+ 450125: '上林县',
+ 450126: '宾阳县',
+ 450127: '横县'
+ },
+ 450200: {
+ 450202: '城中区',
+ 450203: '鱼峰区',
+ 450204: '柳南区',
+ 450205: '柳北区',
+ 450221: '柳江县',
+ 450222: '柳城县',
+ 450223: '鹿寨县',
+ 450224: '融安县',
+ 450225: '融水苗族自治县',
+ 450226: '三江侗族自治县'
+ },
+ 450300: {
+ 450302: '秀峰区',
+ 450303: '叠彩区',
+ 450304: '象山区',
+ 450305: '七星区',
+ 450311: '雁山区',
+ 450312: '临桂区',
+ 450321: '阳朔县',
+ 450323: '灵川县',
+ 450324: '全州县',
+ 450325: '兴安县',
+ 450326: '永福县',
+ 450327: '灌阳县',
+ 450328: '龙胜各族自治县',
+ 450329: '资源县',
+ 450330: '平乐县',
+ 450331: '荔浦县',
+ 450332: '恭城瑶族自治县'
+ },
+ 450400: {
+ 450403: '万秀区',
+ 450405: '长洲区',
+ 450406: '龙圩区',
+ 450421: '苍梧县',
+ 450422: '藤县',
+ 450423: '蒙山县',
+ 450481: '岑溪市'
+ },
+ 450500: {
+ 450502: '海城区',
+ 450503: '银海区',
+ 450512: '铁山港区',
+ 450521: '合浦县'
+ },
+ 450600: {
+ 450602: '港口区',
+ 450603: '防城区',
+ 450621: '上思县',
+ 450681: '东兴市'
+ },
+ 450700: {
+ 450702: '钦南区',
+ 450703: '钦北区',
+ 450721: '灵山县',
+ 450722: '浦北县'
+ },
+ 450800: {
+ 450802: '港北区',
+ 450803: '港南区',
+ 450804: '覃塘区',
+ 450821: '平南县',
+ 450881: '桂平市'
+ },
+ 450900: {
+ 450902: '玉州区',
+ 450903: '福绵区',
+ 450921: '容县',
+ 450922: '陆川县',
+ 450923: '博白县',
+ 450924: '兴业县',
+ 450981: '北流市'
+ },
+ 451000: {
+ 451002: '右江区',
+ 451021: '田阳县',
+ 451022: '田东县',
+ 451023: '平果县',
+ 451024: '德保县',
+ 451025: '靖西县',
+ 451026: '那坡县',
+ 451027: '凌云县',
+ 451028: '乐业县',
+ 451029: '田林县',
+ 451030: '西林县',
+ 451031: '隆林各族自治县'
+ },
+ 451100: {
+ 451102: '八步区',
+ 451121: '昭平县',
+ 451122: '钟山县',
+ 451123: '富川瑶族自治县'
+ },
+ 451200: {
+ 451202: '金城江区',
+ 451221: '南丹县',
+ 451222: '天峨县',
+ 451223: '凤山县',
+ 451224: '东兰县',
+ 451225: '罗城仫佬族自治县',
+ 451226: '环江毛南族自治县',
+ 451227: '巴马瑶族自治县',
+ 451228: '都安瑶族自治县',
+ 451229: '大化瑶族自治县',
+ 451281: '宜州市'
+ },
+ 451300: {
+ 451302: '兴宾区',
+ 451321: '忻城县',
+ 451322: '象州县',
+ 451323: '武宣县',
+ 451324: '金秀瑶族自治县',
+ 451381: '合山市'
+ },
+ 451400: {
+ 451402: '江州区',
+ 451421: '扶绥县',
+ 451422: '宁明县',
+ 451423: '龙州县',
+ 451424: '大新县',
+ 451425: '天等县',
+ 451481: '凭祥市'
+ },
+ 460000: {
+ 460100: '海口市',
+ 460200: '三亚市',
+ 460300: '三沙市',
+ 460400: '儋州市',
+ 469001: '五指山市',
+ 469002: '琼海市',
+ 469005: '文昌市',
+ 469006: '万宁市',
+ 469007: '东方市',
+ 469021: '定安县',
+ 469022: '屯昌县',
+ 469023: '澄迈县',
+ 469024: '临高县',
+ 469025: '白沙黎族自治县',
+ 469026: '昌江黎族自治县',
+ 469027: '乐东黎族自治县',
+ 469028: '陵水黎族自治县',
+ 469029: '保亭黎族苗族自治县',
+ 469030: '琼中黎族苗族自治县'
+ },
+ 460100: {
+ 460105: '秀英区',
+ 460106: '龙华区',
+ 460107: '琼山区',
+ 460108: '美兰区'
+ },
+ 460200: {
+ 460200: '三亚湾',
+ 460202: '海棠区',
+ 460203: '吉阳区',
+ 460204: '天涯区',
+ 460205: '崖州区'
+ },
+ 460300: {
+ 460321: '西沙群岛',
+ 460322: '南沙群岛',
+ 460323: '中沙群岛的岛礁及其海域'
+ },
+ 500000: {
+ 500100: '重庆市',
+ },
+ 500100: {
+ 500101: '万州区',
+ 500102: '涪陵区',
+ 500103: '渝中区',
+ 500104: '大渡口区',
+ 500105: '江北区',
+ 500106: '沙坪坝区',
+ 500107: '九龙坡区',
+ 500108: '南岸区',
+ 500109: '北碚区',
+ 500110: '綦江区',
+ 500111: '大足区',
+ 500112: '渝北区',
+ 500113: '巴南区',
+ 500114: '黔江区',
+ 500115: '长寿区',
+ 500116: '江津区',
+ 500117: '合川区',
+ 500118: '永川区',
+ 500119: '南川区',
+ 500120: '璧山区',
+ 500151: '铜梁区',
+ 500223: '潼南区',
+ 500226: '荣昌区',
+ 500228: '梁平县',
+ 500229: '城口县',
+ 500230: '丰都县',
+ 500231: '垫江县',
+ 500232: '武隆县',
+ 500233: '忠县',
+ 500234: '开县',
+ 500235: '云阳县',
+ 500236: '奉节县',
+ 500237: '巫山县',
+ 500238: '巫溪县',
+ 500240: '石柱土家族自治县',
+ 500241: '秀山土家族苗族自治县',
+ 500242: '酉阳土家族苗族自治县',
+ 500243: '彭水苗族土家族自治县'
+ },
+ 510000: {
+ 510100: '成都市',
+ 510300: '自贡市',
+ 510400: '攀枝花市',
+ 510500: '泸州市',
+ 510600: '德阳市',
+ 510700: '绵阳市',
+ 510800: '广元市',
+ 510900: '遂宁市',
+ 511000: '内江市',
+ 511100: '乐山市',
+ 511300: '南充市',
+ 511400: '眉山市',
+ 511500: '宜宾市',
+ 511600: '广安市',
+ 511700: '达州市',
+ 511800: '雅安市',
+ 511900: '巴中市',
+ 512000: '资阳市',
+ 513200: '阿坝藏族羌族自治州',
+ 513300: '甘孜藏族自治州',
+ 513400: '凉山彝族自治州'
+ },
+ 510100: {
+ 510104: '锦江区',
+ 510105: '青羊区',
+ 510106: '金牛区',
+ 510107: '武侯区',
+ 510108: '成华区',
+ 510112: '龙泉驿区',
+ 510113: '青白江区',
+ 510114: '新都区',
+ 510115: '温江区',
+ 510121: '金堂县',
+ 510122: '双流县',
+ 510124: '郫县',
+ 510129: '大邑县',
+ 510131: '蒲江县',
+ 510132: '新津县',
+ 510181: '都江堰市',
+ 510182: '彭州市',
+ 510183: '邛崃市',
+ 510184: '崇州市'
+ },
+ 510300: {
+ 510302: '自流井区',
+ 510303: '贡井区',
+ 510304: '大安区',
+ 510311: '沿滩区',
+ 510321: '荣县',
+ 510322: '富顺县'
+ },
+ 510400: {
+ 510402: '东区',
+ 510403: '西区',
+ 510411: '仁和区',
+ 510421: '米易县',
+ 510422: '盐边县'
+ },
+ 510500: {
+ 510502: '江阳区',
+ 510503: '纳溪区',
+ 510504: '龙马潭区',
+ 510521: '泸县',
+ 510522: '合江县',
+ 510524: '叙永县',
+ 510525: '古蔺县'
+ },
+ 510600: {
+ 510603: '旌阳区',
+ 510623: '中江县',
+ 510626: '罗江县',
+ 510681: '广汉市',
+ 510682: '什邡市',
+ 510683: '绵竹市'
+ },
+ 510700: {
+ 510703: '涪城区',
+ 510704: '游仙区',
+ 510722: '三台县',
+ 510723: '盐亭县',
+ 510724: '安县',
+ 510725: '梓潼县',
+ 510726: '北川羌族自治县',
+ 510727: '平武县',
+ 510781: '江油市'
+ },
+ 510800: {
+ 510802: '利州区',
+ 510811: '昭化区',
+ 510812: '朝天区',
+ 510821: '旺苍县',
+ 510822: '青川县',
+ 510823: '剑阁县',
+ 510824: '苍溪县'
+ },
+ 510900: {
+ 510903: '船山区',
+ 510904: '安居区',
+ 510921: '蓬溪县',
+ 510922: '射洪县',
+ 510923: '大英县'
+ },
+ 511000: {
+ 511002: '市中区',
+ 511011: '东兴区',
+ 511024: '威远县',
+ 511025: '资中县',
+ 511028: '隆昌县'
+ },
+ 511100: {
+ 511102: '市中区',
+ 511111: '沙湾区',
+ 511112: '五通桥区',
+ 511113: '金口河区',
+ 511123: '犍为县',
+ 511124: '井研县',
+ 511126: '夹江县',
+ 511129: '沐川县',
+ 511132: '峨边彝族自治县',
+ 511133: '马边彝族自治县',
+ 511181: '峨眉山市'
+ },
+ 511300: {
+ 511302: '顺庆区',
+ 511303: '高坪区',
+ 511304: '嘉陵区',
+ 511321: '南部县',
+ 511322: '营山县',
+ 511323: '蓬安县',
+ 511324: '仪陇县',
+ 511325: '西充县',
+ 511381: '阆中市'
+ },
+ 511400: {
+ 511402: '东坡区',
+ 511403: '彭山区',
+ 511421: '仁寿县',
+ 511423: '洪雅县',
+ 511424: '丹棱县',
+ 511425: '青神县'
+ },
+ 511500: {
+ 511502: '翠屏区',
+ 511503: '南溪区',
+ 511521: '宜宾县',
+ 511523: '江安县',
+ 511524: '长宁县',
+ 511525: '高县',
+ 511526: '珙县',
+ 511527: '筠连县',
+ 511528: '兴文县',
+ 511529: '屏山县'
+ },
+ 511600: {
+ 511602: '广安区',
+ 511603: '前锋区',
+ 511621: '岳池县',
+ 511622: '武胜县',
+ 511623: '邻水县',
+ 511681: '华蓥市'
+ },
+ 511700: {
+ 511702: '通川区',
+ 511703: '达川区',
+ 511722: '宣汉县',
+ 511723: '开江县',
+ 511724: '大竹县',
+ 511725: '渠县',
+ 511781: '万源市'
+ },
+ 511800: {
+ 511802: '雨城区',
+ 511803: '名山区',
+ 511822: '荥经县',
+ 511823: '汉源县',
+ 511824: '石棉县',
+ 511825: '天全县',
+ 511826: '芦山县',
+ 511827: '宝兴县'
+ },
+ 511900: {
+ 511902: '巴州区',
+ 511903: '恩阳区',
+ 511921: '通江县',
+ 511922: '南江县',
+ 511923: '平昌县'
+ },
+ 512000: {
+ 512002: '雁江区',
+ 512021: '安岳县',
+ 512022: '乐至县',
+ 512081: '简阳市'
+ },
+ 513200: {
+ 513221: '汶川县',
+ 513222: '理县',
+ 513223: '茂县',
+ 513224: '松潘县',
+ 513225: '九寨沟县',
+ 513226: '金川县',
+ 513227: '小金县',
+ 513228: '黑水县',
+ 513229: '马尔康县',
+ 513230: '壤塘县',
+ 513231: '阿坝县',
+ 513232: '若尔盖县',
+ 513233: '红原县'
+ },
+ 513300: {
+ 513301: '康定市',
+ 513322: '泸定县',
+ 513323: '丹巴县',
+ 513324: '九龙县',
+ 513325: '雅江县',
+ 513326: '道孚县',
+ 513327: '炉霍县',
+ 513328: '甘孜县',
+ 513329: '新龙县',
+ 513330: '德格县',
+ 513331: '白玉县',
+ 513332: '石渠县',
+ 513333: '色达县',
+ 513334: '理塘县',
+ 513335: '巴塘县',
+ 513336: '乡城县',
+ 513337: '稻城县',
+ 513338: '得荣县'
+ },
+ 513400: {
+ 513401: '西昌市',
+ 513422: '木里藏族自治县',
+ 513423: '盐源县',
+ 513424: '德昌县',
+ 513425: '会理县',
+ 513426: '会东县',
+ 513427: '宁南县',
+ 513428: '普格县',
+ 513429: '布拖县',
+ 513430: '金阳县',
+ 513431: '昭觉县',
+ 513432: '喜德县',
+ 513433: '冕宁县',
+ 513434: '越西县',
+ 513435: '甘洛县',
+ 513436: '美姑县',
+ 513437: '雷波县'
+ },
+ 520000: {
+ 520100: '贵阳市',
+ 520200: '六盘水市',
+ 520300: '遵义市',
+ 520400: '安顺市',
+ 520500: '毕节市',
+ 520600: '铜仁市',
+ 522300: '黔西南布依族苗族自治州',
+ 522600: '黔东南苗族侗族自治州',
+ 522700: '黔南布依族苗族自治州'
+ },
+ 520100: {
+ 520102: '南明区',
+ 520103: '云岩区',
+ 520111: '花溪区',
+ 520112: '乌当区',
+ 520113: '白云区',
+ 520115: '观山湖区',
+ 520121: '开阳县',
+ 520122: '息烽县',
+ 520123: '修文县',
+ 520181: '清镇市'
+ },
+ 520200: {
+ 520201: '钟山区',
+ 520203: '六枝特区',
+ 520221: '水城县',
+ 520222: '盘县'
+ },
+ 520300: {
+ 520302: '红花岗区',
+ 520303: '汇川区',
+ 520321: '遵义县',
+ 520322: '桐梓县',
+ 520323: '绥阳县',
+ 520324: '正安县',
+ 520325: '道真仡佬族苗族自治县',
+ 520326: '务川仡佬族苗族自治县',
+ 520327: '凤冈县',
+ 520328: '湄潭县',
+ 520329: '余庆县',
+ 520330: '习水县',
+ 520381: '赤水市',
+ 520382: '仁怀市'
+ },
+ 520400: {
+ 520402: '西秀区',
+ 520403: '平坝区',
+ 520422: '普定县',
+ 520423: '镇宁布依族苗族自治县',
+ 520424: '关岭布依族苗族自治县',
+ 520425: '紫云苗族布依族自治县'
+ },
+ 520500: {
+ 520502: '七星关区',
+ 520521: '大方县',
+ 520522: '黔西县',
+ 520523: '金沙县',
+ 520524: '织金县',
+ 520525: '纳雍县',
+ 520526: '威宁彝族回族苗族自治县',
+ 520527: '赫章县'
+ },
+ 520600: {
+ 520602: '碧江区',
+ 520603: '万山区',
+ 520621: '江口县',
+ 520622: '玉屏侗族自治县',
+ 520623: '石阡县',
+ 520624: '思南县',
+ 520625: '印江土家族苗族自治县',
+ 520626: '德江县',
+ 520627: '沿河土家族自治县',
+ 520628: '松桃苗族自治县'
+ },
+ 522300: {
+ 522301: '兴义市',
+ 522322: '兴仁县',
+ 522323: '普安县',
+ 522324: '晴隆县',
+ 522325: '贞丰县',
+ 522326: '望谟县',
+ 522327: '册亨县',
+ 522328: '安龙县'
+ },
+ 522600: {
+ 522601: '凯里市',
+ 522622: '黄平县',
+ 522623: '施秉县',
+ 522624: '三穗县',
+ 522625: '镇远县',
+ 522626: '岑巩县',
+ 522627: '天柱县',
+ 522628: '锦屏县',
+ 522629: '剑河县',
+ 522630: '台江县',
+ 522631: '黎平县',
+ 522632: '榕江县',
+ 522633: '从江县',
+ 522634: '雷山县',
+ 522635: '麻江县',
+ 522636: '丹寨县'
+ },
+ 522700: {
+ 522701: '都匀市',
+ 522702: '福泉市',
+ 522722: '荔波县',
+ 522723: '贵定县',
+ 522725: '瓮安县',
+ 522726: '独山县',
+ 522727: '平塘县',
+ 522728: '罗甸县',
+ 522729: '长顺县',
+ 522730: '龙里县',
+ 522731: '惠水县',
+ 522732: '三都水族自治县'
+ },
+ 530000: {
+ 530100: '昆明市',
+ 530300: '曲靖市',
+ 530400: '玉溪市',
+ 530500: '保山市',
+ 530600: '昭通市',
+ 530700: '丽江市',
+ 530800: '普洱市',
+ 530900: '临沧市',
+ 532300: '楚雄彝族自治州',
+ 532500: '红河哈尼族彝族自治州',
+ 532600: '文山壮族苗族自治州',
+ 532800: '西双版纳傣族自治州',
+ 532900: '大理白族自治州',
+ 533100: '德宏傣族景颇族自治州',
+ 533300: '怒江傈僳族自治州',
+ 533400: '迪庆藏族自治州'
+ },
+ 530100: {
+ 530102: '五华区',
+ 530103: '盘龙区',
+ 530111: '官渡区',
+ 530112: '西山区',
+ 530113: '东川区',
+ 530114: '呈贡区',
+ 530122: '晋宁县',
+ 530124: '富民县',
+ 530125: '宜良县',
+ 530126: '石林彝族自治县',
+ 530127: '嵩明县',
+ 530128: '禄劝彝族苗族自治县',
+ 530129: '寻甸回族彝族自治县',
+ 530181: '安宁市'
+ },
+ 530300: {
+ 530302: '麒麟区',
+ 530321: '马龙县',
+ 530322: '陆良县',
+ 530323: '师宗县',
+ 530324: '罗平县',
+ 530325: '富源县',
+ 530326: '会泽县',
+ 530328: '沾益县',
+ 530381: '宣威市'
+ },
+ 530400: {
+ 530402: '红塔区',
+ 530421: '江川县',
+ 530422: '澄江县',
+ 530423: '通海县',
+ 530424: '华宁县',
+ 530425: '易门县',
+ 530426: '峨山彝族自治县',
+ 530427: '新平彝族傣族自治县',
+ 530428: '元江哈尼族彝族傣族自治县'
+ },
+ 530500: {
+ 530502: '隆阳区',
+ 530521: '施甸县',
+ 530522: '腾冲县',
+ 530523: '龙陵县',
+ 530524: '昌宁县'
+ },
+ 530600: {
+ 530602: '昭阳区',
+ 530621: '鲁甸县',
+ 530622: '巧家县',
+ 530623: '盐津县',
+ 530624: '大关县',
+ 530625: '永善县',
+ 530626: '绥江县',
+ 530627: '镇雄县',
+ 530628: '彝良县',
+ 530629: '威信县',
+ 530630: '水富县'
+ },
+ 530700: {
+ 530702: '古城区',
+ 530721: '玉龙纳西族自治县',
+ 530722: '永胜县',
+ 530723: '华坪县',
+ 530724: '宁蒗彝族自治县'
+ },
+ 530800: {
+ 530802: '思茅区',
+ 530821: '宁洱哈尼族彝族自治县',
+ 530822: '墨江哈尼族自治县',
+ 530823: '景东彝族自治县',
+ 530824: '景谷傣族彝族自治县',
+ 530825: '镇沅彝族哈尼族拉祜族自治县',
+ 530826: '江城哈尼族彝族自治县',
+ 530827: '孟连傣族拉祜族佤族自治县',
+ 530828: '澜沧拉祜族自治县',
+ 530829: '西盟佤族自治县'
+ },
+ 530900: {
+ 530902: '临翔区',
+ 530921: '凤庆县',
+ 530922: '云县',
+ 530923: '永德县',
+ 530924: '镇康县',
+ 530925: '双江拉祜族佤族布朗族傣族自治县',
+ 530926: '耿马傣族佤族自治县',
+ 530927: '沧源佤族自治县'
+ },
+ 532300: {
+ 532301: '楚雄市',
+ 532322: '双柏县',
+ 532323: '牟定县',
+ 532324: '南华县',
+ 532325: '姚安县',
+ 532326: '大姚县',
+ 532327: '永仁县',
+ 532328: '元谋县',
+ 532329: '武定县',
+ 532331: '禄丰县'
+ },
+ 532500: {
+ 532501: '个旧市',
+ 532502: '开远市',
+ 532503: '蒙自市',
+ 532504: '弥勒市',
+ 532523: '屏边苗族自治县',
+ 532524: '建水县',
+ 532525: '石屏县',
+ 532527: '泸西县',
+ 532528: '元阳县',
+ 532529: '红河县',
+ 532530: '金平苗族瑶族傣族自治县',
+ 532531: '绿春县',
+ 532532: '河口瑶族自治县'
+ },
+ 532600: {
+ 532601: '文山市',
+ 532622: '砚山县',
+ 532623: '西畴县',
+ 532624: '麻栗坡县',
+ 532625: '马关县',
+ 532626: '丘北县',
+ 532627: '广南县',
+ 532628: '富宁县'
+ },
+ 532800: {
+ 532801: '景洪市',
+ 532822: '勐海县',
+ 532823: '勐腊县'
+ },
+ 532900: {
+ 532901: '大理市',
+ 532922: '漾濞彝族自治县',
+ 532923: '祥云县',
+ 532924: '宾川县',
+ 532925: '弥渡县',
+ 532926: '南涧彝族自治县',
+ 532927: '巍山彝族回族自治县',
+ 532928: '永平县',
+ 532929: '云龙县',
+ 532930: '洱源县',
+ 532931: '剑川县',
+ 532932: '鹤庆县'
+ },
+ 533100: {
+ 533102: '瑞丽市',
+ 533103: '芒市',
+ 533122: '梁河县',
+ 533123: '盈江县',
+ 533124: '陇川县'
+ },
+ 533300: {
+ 533321: '泸水县',
+ 533323: '福贡县',
+ 533324: '贡山独龙族怒族自治县',
+ 533325: '兰坪白族普米族自治县'
+ },
+ 533400: {
+ 533401: '香格里拉市',
+ 533422: '德钦县',
+ 533423: '维西傈僳族自治县'
+ },
+ 540000: {
+ 540100: '拉萨市',
+ 540200: '日喀则市',
+ 540300: '昌都市',
+ 542200: '山南地区',
+ 542400: '那曲地区',
+ 542500: '阿里地区',
+ 542600: '林芝市'
+ },
+ 540100: {
+ 540102: '城关区',
+ 540121: '林周县',
+ 540122: '当雄县',
+ 540123: '尼木县',
+ 540124: '曲水县',
+ 540125: '堆龙德庆县',
+ 540126: '达孜县',
+ 540127: '墨竹工卡县'
+ },
+ 540200: {
+ 540202: '桑珠孜区',
+ 540221: '南木林县',
+ 540222: '江孜县',
+ 540223: '定日县',
+ 540224: '萨迦县',
+ 540225: '拉孜县',
+ 540226: '昂仁县',
+ 540227: '谢通门县',
+ 540228: '白朗县',
+ 540229: '仁布县',
+ 540230: '康马县',
+ 540231: '定结县',
+ 540232: '仲巴县',
+ 540233: '亚东县',
+ 540234: '吉隆县',
+ 540235: '聂拉木县',
+ 540236: '萨嘎县',
+ 540237: '岗巴县'
+ },
+ 540300: {
+ 540302: '卡若区',
+ 540321: '江达县',
+ 540322: '贡觉县',
+ 540323: '类乌齐县',
+ 540324: '丁青县',
+ 540325: '察雅县',
+ 540326: '八宿县',
+ 540327: '左贡县',
+ 540328: '芒康县',
+ 540329: '洛隆县',
+ 540330: '边坝县'
+ },
+ 542200: {
+ 542221: '乃东县',
+ 542222: '扎囊县',
+ 542223: '贡嘎县',
+ 542224: '桑日县',
+ 542225: '琼结县',
+ 542226: '曲松县',
+ 542227: '措美县',
+ 542228: '洛扎县',
+ 542229: '加查县',
+ 542231: '隆子县',
+ 542232: '错那县',
+ 542233: '浪卡子县'
+ },
+ 542400: {
+ 542421: '那曲县',
+ 542422: '嘉黎县',
+ 542423: '比如县',
+ 542424: '聂荣县',
+ 542425: '安多县',
+ 542426: '申扎县',
+ 542427: '索县',
+ 542428: '班戈县',
+ 542429: '巴青县',
+ 542430: '尼玛县',
+ 542431: '双湖县'
+ },
+ 542500: {
+ 542521: '普兰县',
+ 542522: '札达县',
+ 542523: '噶尔县',
+ 542524: '日土县',
+ 542525: '革吉县',
+ 542526: '改则县',
+ 542527: '措勤县'
+ },
+ 542600: {
+ 542621: '巴宜区',
+ 542622: '工布江达县',
+ 542623: '米林县',
+ 542624: '墨脱县',
+ 542625: '波密县',
+ 542626: '察隅县',
+ 542627: '朗县'
+ },
+ 610000: {
+ 610100: '西安市',
+ 610200: '铜川市',
+ 610300: '宝鸡市',
+ 610400: '咸阳市',
+ 610500: '渭南市',
+ 610600: '延安市',
+ 610700: '汉中市',
+ 610800: '榆林市',
+ 610900: '安康市',
+ 611000: '商洛市'
+ },
+ 610100: {
+ 610102: '新城区',
+ 610103: '碑林区',
+ 610104: '莲湖区',
+ 610111: '灞桥区',
+ 610112: '未央区',
+ 610113: '雁塔区',
+ 610114: '阎良区',
+ 610115: '临潼区',
+ 610116: '长安区',
+ 610117: '高陵区',
+ 610122: '蓝田县',
+ 610124: '周至县',
+ 610125: '户县'
+ },
+ 610200: {
+ 610202: '王益区',
+ 610203: '印台区',
+ 610204: '耀州区',
+ 610222: '宜君县'
+ },
+ 610300: {
+ 610302: '渭滨区',
+ 610303: '金台区',
+ 610304: '陈仓区',
+ 610322: '凤翔县',
+ 610323: '岐山县',
+ 610324: '扶风县',
+ 610326: '眉县',
+ 610327: '陇县',
+ 610328: '千阳县',
+ 610329: '麟游县',
+ 610330: '凤县',
+ 610331: '太白县'
+ },
+ 610400: {
+ 610402: '秦都区',
+ 610403: '杨陵区',
+ 610404: '渭城区',
+ 610422: '三原县',
+ 610423: '泾阳县',
+ 610424: '乾县',
+ 610425: '礼泉县',
+ 610426: '永寿县',
+ 610427: '彬县',
+ 610428: '长武县',
+ 610429: '旬邑县',
+ 610430: '淳化县',
+ 610431: '武功县',
+ 610481: '兴平市'
+ },
+ 610500: {
+ 610502: '临渭区',
+ 610521: '华县',
+ 610522: '潼关县',
+ 610523: '大荔县',
+ 610524: '合阳县',
+ 610525: '澄城县',
+ 610526: '蒲城县',
+ 610527: '白水县',
+ 610528: '富平县',
+ 610581: '韩城市',
+ 610582: '华阴市'
+ },
+ 610600: {
+ 610602: '宝塔区',
+ 610621: '延长县',
+ 610622: '延川县',
+ 610623: '子长县',
+ 610624: '安塞县',
+ 610625: '志丹县',
+ 610626: '吴起县',
+ 610627: '甘泉县',
+ 610628: '富县',
+ 610629: '洛川县',
+ 610630: '宜川县',
+ 610631: '黄龙县',
+ 610632: '黄陵县'
+ },
+ 610700: {
+ 610702: '汉台区',
+ 610721: '南郑县',
+ 610722: '城固县',
+ 610723: '洋县',
+ 610724: '西乡县',
+ 610725: '勉县',
+ 610726: '宁强县',
+ 610727: '略阳县',
+ 610728: '镇巴县',
+ 610729: '留坝县',
+ 610730: '佛坪县'
+ },
+ 610800: {
+ 610802: '榆阳区',
+ 610821: '神木县',
+ 610822: '府谷县',
+ 610823: '横山县',
+ 610824: '靖边县',
+ 610825: '定边县',
+ 610826: '绥德县',
+ 610827: '米脂县',
+ 610828: '佳县',
+ 610829: '吴堡县',
+ 610830: '清涧县',
+ 610831: '子洲县'
+ },
+ 610900: {
+ 610902: '汉滨区',
+ 610921: '汉阴县',
+ 610922: '石泉县',
+ 610923: '宁陕县',
+ 610924: '紫阳县',
+ 610925: '岚皋县',
+ 610926: '平利县',
+ 610927: '镇坪县',
+ 610928: '旬阳县',
+ 610929: '白河县'
+ },
+ 611000: {
+ 611002: '商州区',
+ 611021: '洛南县',
+ 611022: '丹凤县',
+ 611023: '商南县',
+ 611024: '山阳县',
+ 611025: '镇安县',
+ 611026: '柞水县'
+ },
+ 620000: {
+ 620100: '兰州市',
+ 620200: '嘉峪关市',
+ 620300: '金昌市',
+ 620400: '白银市',
+ 620500: '天水市',
+ 620600: '武威市',
+ 620700: '张掖市',
+ 620800: '平凉市',
+ 620900: '酒泉市',
+ 621000: '庆阳市',
+ 621100: '定西市',
+ 621200: '陇南市',
+ 622900: '临夏回族自治州',
+ 623000: '甘南藏族自治州'
+ },
+ 620100: {
+ 620102: '城关区',
+ 620103: '七里河区',
+ 620104: '西固区',
+ 620105: '安宁区',
+ 620111: '红古区',
+ 620121: '永登县',
+ 620122: '皋兰县',
+ 620123: '榆中县'
+ },
+ 620300: {
+ 620302: '金川区',
+ 620321: '永昌县'
+ },
+ 620400: {
+ 620402: '白银区',
+ 620403: '平川区',
+ 620421: '靖远县',
+ 620422: '会宁县',
+ 620423: '景泰县'
+ },
+ 620500: {
+ 620502: '秦州区',
+ 620503: '麦积区',
+ 620521: '清水县',
+ 620522: '秦安县',
+ 620523: '甘谷县',
+ 620524: '武山县',
+ 620525: '张家川回族自治县'
+ },
+ 620600: {
+ 620602: '凉州区',
+ 620621: '民勤县',
+ 620622: '古浪县',
+ 620623: '天祝藏族自治县'
+ },
+ 620700: {
+ 620702: '甘州区',
+ 620721: '肃南裕固族自治县',
+ 620722: '民乐县',
+ 620723: '临泽县',
+ 620724: '高台县',
+ 620725: '山丹县'
+ },
+ 620800: {
+ 620802: '崆峒区',
+ 620821: '泾川县',
+ 620822: '灵台县',
+ 620823: '崇信县',
+ 620824: '华亭县',
+ 620825: '庄浪县',
+ 620826: '静宁县'
+ },
+ 620900: {
+ 620902: '肃州区',
+ 620921: '金塔县',
+ 620922: '瓜州县',
+ 620923: '肃北蒙古族自治县',
+ 620924: '阿克塞哈萨克族自治县',
+ 620981: '玉门市',
+ 620982: '敦煌市'
+ },
+ 621000: {
+ 621002: '西峰区',
+ 621021: '庆城县',
+ 621022: '环县',
+ 621023: '华池县',
+ 621024: '合水县',
+ 621025: '正宁县',
+ 621026: '宁县',
+ 621027: '镇原县'
+ },
+ 621100: {
+ 621102: '安定区',
+ 621121: '通渭县',
+ 621122: '陇西县',
+ 621123: '渭源县',
+ 621124: '临洮县',
+ 621125: '漳县',
+ 621126: '岷县'
+ },
+ 621200: {
+ 621202: '武都区',
+ 621221: '成县',
+ 621222: '文县',
+ 621223: '宕昌县',
+ 621224: '康县',
+ 621225: '西和县',
+ 621226: '礼县',
+ 621227: '徽县',
+ 621228: '两当县'
+ },
+ 622900: {
+ 622901: '临夏市',
+ 622921: '临夏县',
+ 622922: '康乐县',
+ 622923: '永靖县',
+ 622924: '广河县',
+ 622925: '和政县',
+ 622926: '东乡族自治县',
+ 622927: '积石山保安族东乡族撒拉族自治县'
+ },
+ 623000: {
+ 623001: '合作市',
+ 623021: '临潭县',
+ 623022: '卓尼县',
+ 623023: '舟曲县',
+ 623024: '迭部县',
+ 623025: '玛曲县',
+ 623026: '碌曲县',
+ 623027: '夏河县'
+ },
+ 630000: {
+ 630100: '西宁市',
+ 630200: '海东市',
+ 632200: '海北藏族自治州',
+ 632300: '黄南藏族自治州',
+ 632500: '海南藏族自治州',
+ 632600: '果洛藏族自治州',
+ 632700: '玉树藏族自治州',
+ 632800: '海西蒙古族藏族自治州'
+ },
+ 630100: {
+ 630102: '城东区',
+ 630103: '城中区',
+ 630104: '城西区',
+ 630105: '城北区',
+ 630121: '大通回族土族自治县',
+ 630122: '湟中县',
+ 630123: '湟源县'
+ },
+ 630200: {
+ 630202: '乐都区',
+ 630203: '平安区',
+ 630222: '民和回族土族自治县',
+ 630223: '互助土族自治县',
+ 630224: '化隆回族自治县',
+ 630225: '循化撒拉族自治县'
+ },
+ 632200: {
+ 632221: '门源回族自治县',
+ 632222: '祁连县',
+ 632223: '海晏县',
+ 632224: '刚察县'
+ },
+ 632300: {
+ 632321: '同仁县',
+ 632322: '尖扎县',
+ 632323: '泽库县',
+ 632324: '河南蒙古族自治县'
+ },
+ 632500: {
+ 632521: '共和县',
+ 632522: '同德县',
+ 632523: '贵德县',
+ 632524: '兴海县',
+ 632525: '贵南县'
+ },
+ 632600: {
+ 632621: '玛沁县',
+ 632622: '班玛县',
+ 632623: '甘德县',
+ 632624: '达日县',
+ 632625: '久治县',
+ 632626: '玛多县'
+ },
+ 632700: {
+ 632701: '玉树市',
+ 632722: '杂多县',
+ 632723: '称多县',
+ 632724: '治多县',
+ 632725: '囊谦县',
+ 632726: '曲麻莱县'
+ },
+ 632800: {
+ 632801: '格尔木市',
+ 632802: '德令哈市',
+ 632821: '乌兰县',
+ 632822: '都兰县',
+ 632823: '天峻县',
+ 632825: '海西蒙古族藏族自治州直辖'
+ },
+ 640000: {
+ 640100: '银川市',
+ 640200: '石嘴山市',
+ 640300: '吴忠市',
+ 640400: '固原市',
+ 640500: '中卫市'
+ },
+ 640100: {
+ 640104: '兴庆区',
+ 640105: '西夏区',
+ 640106: '金凤区',
+ 640121: '永宁县',
+ 640122: '贺兰县',
+ 640181: '灵武市'
+ },
+ 640200: {
+ 640202: '大武口区',
+ 640205: '惠农区',
+ 640221: '平罗县'
+ },
+ 640300: {
+ 640302: '利通区',
+ 640303: '红寺堡区',
+ 640323: '盐池县',
+ 640324: '同心县',
+ 640381: '青铜峡市'
+ },
+ 640400: {
+ 640402: '原州区',
+ 640422: '西吉县',
+ 640423: '隆德县',
+ 640424: '泾源县',
+ 640425: '彭阳县'
+ },
+ 640500: {
+ 640502: '沙坡头区',
+ 640521: '中宁县',
+ 640522: '海原县'
+ },
+ 650000: {
+ 650100: '乌鲁木齐市',
+ 650200: '克拉玛依市',
+ 652100: '吐鲁番市',
+ 652200: '哈密地区',
+ 652300: '昌吉回族自治州',
+ 652700: '博尔塔拉蒙古自治州',
+ 652800: '巴音郭楞蒙古自治州',
+ 652900: '阿克苏地区',
+ 653000: '克孜勒苏柯尔克孜自治州',
+ 653100: '喀什地区',
+ 653200: '和田地区',
+ 654000: '伊犁哈萨克自治州',
+ 654200: '塔城地区',
+ 654300: '阿勒泰地区',
+ 659001: '石河子市',
+ 659002: '阿拉尔市',
+ 659003: '图木舒克市',
+ 659004: '五家渠市',
+ 659005: '北屯市',
+ 659006: '铁门关市',
+ 659007: '双河市',
+ 659008: '可克达拉市'
+ },
+ 650100: {
+ 650102: '天山区',
+ 650103: '沙依巴克区',
+ 650104: '新市区',
+ 650105: '水磨沟区',
+ 650106: '头屯河区',
+ 650107: '达坂城区',
+ 650109: '米东区',
+ 650121: '乌鲁木齐县'
+ },
+ 650200: {
+ 650202: '独山子区',
+ 650203: '克拉玛依区',
+ 650204: '白碱滩区',
+ 650205: '乌尔禾区'
+ },
+ 652100: {
+ 652101: '高昌区',
+ 652122: '鄯善县',
+ 652123: '托克逊县'
+ },
+ 652200: {
+ 652201: '哈密市',
+ 652222: '巴里坤哈萨克自治县',
+ 652223: '伊吾县'
+ },
+ 652300: {
+ 652301: '昌吉市',
+ 652302: '阜康市',
+ 652323: '呼图壁县',
+ 652324: '玛纳斯县',
+ 652325: '奇台县',
+ 652327: '吉木萨尔县',
+ 652328: '木垒哈萨克自治县'
+ },
+ 652700: {
+ 652701: '博乐市',
+ 652702: '阿拉山口市',
+ 652722: '精河县',
+ 652723: '温泉县'
+ },
+ 652800: {
+ 652801: '库尔勒市',
+ 652822: '轮台县',
+ 652823: '尉犁县',
+ 652824: '若羌县',
+ 652825: '且末县',
+ 652826: '焉耆回族自治县',
+ 652827: '和静县',
+ 652828: '和硕县',
+ 652829: '博湖县'
+ },
+ 652900: {
+ 652901: '阿克苏市',
+ 652922: '温宿县',
+ 652923: '库车县',
+ 652924: '沙雅县',
+ 652925: '新和县',
+ 652926: '拜城县',
+ 652927: '乌什县',
+ 652928: '阿瓦提县',
+ 652929: '柯坪县'
+ },
+ 653000: {
+ 653001: '阿图什市',
+ 653022: '阿克陶县',
+ 653023: '阿合奇县',
+ 653024: '乌恰县'
+ },
+ 653100: {
+ 653101: '喀什市',
+ 653121: '疏附县',
+ 653122: '疏勒县',
+ 653123: '英吉沙县',
+ 653124: '泽普县',
+ 653125: '莎车县',
+ 653126: '叶城县',
+ 653127: '麦盖提县',
+ 653128: '岳普湖县',
+ 653129: '伽师县',
+ 653130: '巴楚县',
+ 653131: '塔什库尔干塔吉克自治县'
+ },
+ 653200: {
+ 653201: '和田市',
+ 653221: '和田县',
+ 653222: '墨玉县',
+ 653223: '皮山县',
+ 653224: '洛浦县',
+ 653225: '策勒县',
+ 653226: '于田县',
+ 653227: '民丰县'
+ },
+ 654000: {
+ 654002: '伊宁市',
+ 654003: '奎屯市',
+ 654004: '霍尔果斯市',
+ 654021: '伊宁县',
+ 654022: '察布查尔锡伯自治县',
+ 654023: '霍城县',
+ 654024: '巩留县',
+ 654025: '新源县',
+ 654026: '昭苏县',
+ 654027: '特克斯县',
+ 654028: '尼勒克县'
+ },
+ 654200: {
+ 654201: '塔城市',
+ 654202: '乌苏市',
+ 654221: '额敏县',
+ 654223: '沙湾县',
+ 654224: '托里县',
+ 654225: '裕民县',
+ 654226: '和布克赛尔蒙古自治县'
+ },
+ 654300: {
+ 654301: '阿勒泰市',
+ 654321: '布尔津县',
+ 654322: '富蕴县',
+ 654323: '福海县',
+ 654324: '哈巴河县',
+ 654325: '青河县',
+ 654326: '吉木乃县'
+ },
+ 810000: {
+ 810001: '中西區',
+ 810002: '灣仔區',
+ 810003: '東區',
+ 810004: '南區',
+ 810005: '油尖旺區',
+ 810006: '深水埗區',
+ 810007: '九龍城區',
+ 810008: '黃大仙區',
+ 810009: '觀塘區',
+ 810010: '荃灣區',
+ 810011: '屯門區',
+ 810012: '元朗區',
+ 810013: '北區',
+ 810014: '大埔區',
+ 810015: '西貢區',
+ 810016: '沙田區',
+ 810017: '葵青區',
+ 810018: '離島區'
+ },
+ 820000: {
+ 820001: '花地瑪堂區',
+ 820002: '花王堂區',
+ 820003: '望德堂區',
+ 820004: '大堂區',
+ 820005: '風順堂區',
+ 820006: '嘉模堂區',
+ 820007: '路氹填海區',
+ 820008: '聖方濟各堂區'
+ }
+ }
+ ;
+
+ if (typeof window !== 'undefined') {
+ window.ChineseDistricts = ChineseDistricts;
+ }
+
+ return ChineseDistricts;
+
+}
diff --git a/public/static/js/clipboard.min.js b/public/static/js/clipboard.min.js
new file mode 100644
index 0000000..95f55d7
--- /dev/null
+++ b/public/static/js/clipboard.min.js
@@ -0,0 +1,7 @@
+/*!
+ * clipboard.js v2.0.8
+ * https://clipboardjs.com/
+ *
+ * Licensed MIT © Zeno Rocha
+ */
+!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return n={134:function(t,e,n){"use strict";n.d(e,{default:function(){return r}});var e=n(279),i=n.n(e),e=n(370),a=n.n(e),e=n(817),o=n.n(e);function c(t){return(c="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function u(t,e){for(var n=0;n天",
+ hourTag: ":",
+ minTag: ":",
+ secTag: ":",
+ dayClass: ".countdownday",
+ hourClass: ".countdownhour",
+ minClass: ".countdownmin",
+ secClass: ".countdownsec",
+ isDefault: false,
+ showTemp:0
+ };
+ var _temp = $.extend(_TempTag, tagTemp);
+ var TIMER;
+ createdom = function (dom) {
+ _DOM = dom;
+ data = Math.round($(dom).attr("data"));
+ var htmlstr = (_temp.showTemp == 0 ? _temp.dayTag : "") + _temp.hourTag + _temp.minTag + _temp.secTag;
+ if (_temp.isDefault) {
+ htmlstr = (_temp.showTemp == 0 ? _defaultTag.dayTag : "") + _defaultTag.hourTag + _defaultTag.minTag + _defaultTag.secTag;
+ htmlstr = "";
+ $("head").append("");
+ }
+ $(_DOM).html(htmlstr);
+ reflash();
+ };
+ reflash = function () {
+ var range = data,
+ secday = 86400, sechour = 3600,
+ days = parseInt(range / secday),
+ hours = _temp.showTemp == 0 ? parseInt((range % secday) / sechour) : parseInt(range / sechour),
+ min = parseInt(((range % secday) % sechour) / 60),
+ sec = ((range % secday) % sechour) % 60;
+ data--;
+ if (range < 0) {
+ window.clearInterval(TIMER); //清楚定时器
+ } else {
+ $(_DOM).find(_temp.dayClass).html(nol(days));
+ $(_DOM).find(_temp.hourClass).html(nol(hours));
+ $(_DOM).find(_temp.minClass).html(nol(min));
+ $(_DOM).find(_temp.secClass).html(nol(sec));
+ }
+ if ((range - 1) == 0) {
+ undefined == backfun ? function () { } : backfun();
+ }
+ };
+ TIMER = setInterval(reflash, 1000);
+ nol = function (h) {
+ return h > 9 ? h : '0' + h;
+ };
+ return this.each(function () {
+ var $box = $(this);
+ createdom($box);
+ });
+ };
+
+})(jQuery);
diff --git a/public/static/js/index.js b/public/static/js/index.js
new file mode 100644
index 0000000..f7c63a1
--- /dev/null
+++ b/public/static/js/index.js
@@ -0,0 +1,13 @@
+$(function(){
+ $('html').css('font-size',$(document).width() >= 750 ? '100px' : $(document).width() / 750 * 100 + 'px');
+ var index = 1;
+ function toRun(){
+ if (index == 2) index = 0;
+ $('#ky .steps3-container ul').animate({
+ left:-index * $('#ky .steps3-container').width() + 'px'
+ })
+ index +=1;
+ $("body").css("opacity",1);
+ }
+ setInterval(toRun,100)
+})
\ No newline at end of file
diff --git a/public/static/js/jquery-3.3.1.js b/public/static/js/jquery-3.3.1.js
new file mode 100644
index 0000000..00c44f0
--- /dev/null
+++ b/public/static/js/jquery-3.3.1.js
@@ -0,0 +1,10447 @@
+/*!
+ * jQuery JavaScript Library v3.3.1
+ * https://jquery.com/
+ *
+ * Includes Sizzle.js
+ * https://sizzlejs.com/
+ *
+ * Copyright JS Foundation and other contributors
+ * Released under the MIT license
+ * https://jquery.org/license
+ *
+ * Date: 2018-01-20T17:24Z
+ */
+(function (global, factory) {
+
+ "use strict";
+
+ if (typeof module === "object" && typeof module.exports === "object") {
+
+ // For CommonJS and CommonJS-like environments where a proper `window`
+ // is present, execute the factory and get jQuery.
+ // For environments that do not have a `window` with a `document`
+ // (such as Node.js), expose a factory as module.exports.
+ // This accentuates the need for the creation of a real `window`.
+ // e.g. var jQuery = require("jquery")(window);
+ // See ticket #14549 for more info.
+ module.exports = global.document ?
+ factory(global, true) :
+ function (w) {
+ if (!w.document) {
+ throw new Error("jQuery requires a window with a document");
+ }
+ return factory(w);
+ };
+ } else {
+ factory(global);
+ }
+
+ // Pass this if window is not defined yet
+})(typeof window !== "undefined" ? window : this, function (window, noGlobal) {
+
+ // Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1
+ // throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode
+ // arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common
+ // enough that all such attempts are guarded in a try block.
+ "use strict";
+
+ var arr = [];
+
+ var document = window.document;
+
+ var getProto = Object.getPrototypeOf;
+
+ var slice = arr.slice;
+
+ var concat = arr.concat;
+
+ var push = arr.push;
+
+ var indexOf = arr.indexOf;
+
+ var class2type = {};
+
+ var toString = class2type.toString;
+
+ var hasOwn = class2type.hasOwnProperty;
+
+ var fnToString = hasOwn.toString;
+
+ var ObjectFunctionString = fnToString.call(Object);
+
+ var support = {};
+
+ var isFunction = function isFunction(obj) {
+
+ // Support: Chrome <=57, Firefox <=52
+ // In some browsers, typeof returns "function" for HTML