master
wangxinglong 2022-05-25 19:35:57 +08:00
commit 4aab76d038
5777 changed files with 716350 additions and 0 deletions

19
.env.bak Normal file
View File

@ -0,0 +1,19 @@
APP_DEBUG = true
APP_TRACE = true
[APP]
DEFAULT_TIMEZONE = Asia/Shanghai
[DATABASE]
TYPE = mysql
HOSTNAME = 127.0.0.1
DATABASE = dev_bee_cms
USERNAME = dev_bee_cms
PASSWORD = dT7yH5fmd28JG6ER
HOSTPORT = 3306
CHARSET = utf8mb4
DEBUG = true
PREFIX = bee_
[LANG]
default_lang = zh-cn

14
.gitignore vendored Normal file
View File

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

32
LICENSE.txt Normal file
View File

@ -0,0 +1,32 @@
ThinkPHP遵循Apache2开源协议发布并提供免费使用。
版权所有Copyright © 2006-2016 by ThinkPHP (http://thinkphp.cn)
All rights reserved。
ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。
Apache Licence是著名的非盈利开源组织Apache采用的协议。
该协议和BSD类似鼓励代码共享和尊重原作者的著作权
允许代码修改,再作为开源或商业软件发布。需要满足
的条件:
1 需要给代码的用户一份Apache Licence
2 如果你修改了代码,需要在被修改的文件中说明;
3 在延伸的代码中(修改和有源代码衍生的代码中)需要
带有原来代码中的协议,商标,专利声明和其他原来作者规
定需要包含的说明;
4 如果再发布的产品中包含一个Notice文件则在Notice文
件中需要带有本协议内容。你可以在Notice中增加自己的
许可但不可以表现为对Apache Licence构成更改。
具体的协议参考http://www.apache.org/licenses/LICENSE-2.0
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

31
README.md Normal file
View File

@ -0,0 +1,31 @@
## 本CMS基于ThinkPHP 6.0.9开发
> - 运行环境要求 PHP7.4+
> - MySql版本限制 5.7.*需要支持json函数
> - [富文本编辑器选择wangEditorv3.1.1](https://github.com/wangfupeng1988/wangEditor)
> - [上传插件选择filepond4.7.1](https://github.com/pqina/filepond)
> - 默认上传插件修改为使用layui组件库自带的
>
<hr /><br />
**PHP扩展依赖列表**
> - fileinfo
> - exif 需要安装在mbsting扩展之后
> - gd
<hr /><br />
**PHP组件列表**
> -
<hr /><br />
**前端组件列表**
> -
<hr /><br />

1
app/.htaccess Normal file
View File

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

674
app/common.php Normal file
View File

@ -0,0 +1,674 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 流年 <liu21st@gmail.com>
// +----------------------------------------------------------------------
use think\Collection;
use think\db\exception\DbException;
use think\exception\ClassNotFoundException;
use think\Model;
use think\Paginator;
// 应用公共文件
if (!function_exists('widget')) {
/**
* 渲染输出Widget
* @param string $name Widget名称
* @param array $data 传入的参数
* @return mixed
* milo 2019-05-08 从TP5.1代码中拿来修改的
*/
function widget($name, $data = [])
{
return action($name, $data, 'widget');
}
}
if (!function_exists('action')) {
/**
* 调用模块的操作方法 参数格式 [模块/控制器/]操作
* @param string $url 调用地址
* @param string|array $vars 调用参数 支持字符串和数组
* @param string $layer 要调用的控制层名称
* @param bool $appendSuffix 是否添加类名后缀
* @return mixed
* milo 2019-05-08 从TP5.1代码中拿来修改的
*/
function action($url, $vars = [], $layer = 'controller', $appendSuffix = false)
{
$info = pathinfo($url);
$action = $info['basename'];
$module = '.' != $info['dirname'] ? $info['dirname'] : request()->controller();
$class = controller($module, $layer);
if (is_scalar($vars)) {
if (strpos($vars, '=')) {
parse_str($vars, $vars);
} else {
$vars = [$vars];
}
}
return app()->invokeMethod([$class, $action.config('route.action_suffix')], $vars);
}
}
if (!function_exists('controller')) {
/**
* 实例化(分层)控制器 格式:[模块名/]控制器名
* @access public
* @param string $name 资源地址
* @param string $layer 控制层名称
* @param bool $appendSuffix 是否添加类名后缀
* @param string $empty 空控制器名称
* @return object
* @throws ClassNotFoundException
*
* milo 2019-05-08 从TP5.1代码中拿来修改的
*/
function controller($name, $layer = 'controller', $empty = '')
{
$class = parseClass($name, $layer);
if (class_exists($class)) {
return app()->make($class);
} elseif ($empty && class_exists($emptyClass = app()->parseClass($layer, $empty))) {
return app()->make($emptyClass);
}
throw new ClassNotFoundException('class not exists:'.$class, $class);
}
}
if (!function_exists('parseClass')) {
/**
* 解析模块和类名
* @access protected
* @param string $name 资源地址
* @param string $layer 验证层名称
* @param bool $appendSuffix 是否添加类名后缀
* @return array
*
* milo 2019-05-08 从TP5.1代码中拿来修改的
*/
function parseClass($name, $layer)
{
if (false !== strpos($name, '\\')) {
$class = $name;
} else {
if (strpos($name, '/')) {
$names = explode('/', $name, 2);
$name = $names[1];
}
$class = app()->parseClass($layer, $name);
}
return $class;
}
}
if (!function_exists('randomStr')) {
/**
* 获取随机字符串
* @param int $type 0:数字(默认)1全部2:小写字母;3:大写字母4字母
* @param int $len 字符串长度
* @return string
*/
function randomStr($type = 0, $len = 5)
{
$strPol = "0123456789";
if ($type == 1) {
$strPol = "ABCDEFGHIJKLMOPQRSTUVWYZ0123456789abcdefghijklmopqrstuvwyz";
} elseif ($type == 2) {
$strPol = "abcdefghijklmopqrstuvwyz";
} elseif ($type == 3) {
$strPol = "ABCDEFGHIJKLMOPQRSTUVWYZ";
} elseif ($type == 4) {
$strPol = "ABCDEFGHIJKLMOPQRSTUVWYZabcdefghijklmopqrstuvwyz";
}
$max = strlen($strPol) - 1;
$str = '';
for ($i = 0; $i < $len; $i++) {
$str .= $strPol[rand(0, $max)];
}
return $str;
}
}
if (!function_exists('isMobile')) {
//判断访问终端是否为移动端
function isMobile()
{
// 如果有HTTP_X_WAP_PROFILE则一定是移动设备
if (isset($_SERVER['HTTP_X_WAP_PROFILE'])) {
return true;
}
// 如果via信息含有wap则一定是移动设备,部分服务商会屏蔽该信息
if (isset($_SERVER['HTTP_VIA'])) {
// 找不到为flase,否则为true
return stristr($_SERVER['HTTP_VIA'], "wap") ? true : false;
}
// 脑残法,判断手机发送的客户端标志,兼容性有待提高。其中'MicroMessenger'是电脑微信
if (isset($_SERVER['HTTP_USER_AGENT'])) {
$clientkeywords = [
'nokia', 'sony', 'ericsson', 'mot', 'samsung', 'htc', 'sgh', 'lg', 'sharp', 'sie-', 'philips',
'panasonic', 'alcatel', 'lenovo', 'iphone', 'ipod', 'blackberry', 'meizu', 'android', 'netfront',
'symbian', 'ucweb', 'windowsce', 'palm', 'operamini', 'operamobi', 'openwave', 'nexusone', 'cldc',
'midp', 'wap', 'mobile', 'MicroMessenger'
];
// 从HTTP_USER_AGENT中查找手机浏览器的关键字
if (preg_match("/(".implode('|', $clientkeywords).")/i", strtolower($_SERVER['HTTP_USER_AGENT']))) {
return true;
}
}
// 协议法,因为有可能不准确,放到最后判断
if (isset ($_SERVER['HTTP_ACCEPT'])) {
// 如果只支持wml并且不支持html那一定是移动设备
// 如果支持wml和html但是wml在html之前则是移动设备
if ((strpos($_SERVER['HTTP_ACCEPT'], 'vnd.wap.wml') !== false) && (strpos($_SERVER['HTTP_ACCEPT'],
'text/html') === false || (strpos($_SERVER['HTTP_ACCEPT'],
'vnd.wap.wml') < strpos($_SERVER['HTTP_ACCEPT'], 'text/html')))) {
return true;
}
}
return false;
}
}
//根据栏目获取路径
if (!function_exists('getUri')) {
function getUri($cate)
{
$url = '';
if (!empty($cate)) {
if ($cate['is_index']) {
$url = '/';
} elseif (!empty($cate['url'])) {
$url = $cate['url'];
} else {
$url = url($cate['template'].'/index', ['category_id' => $cate['id']]);
}
}
return $url;
}
}
//根据文件大小转换为文字
if (!function_exists('sizeToStr')) {
function sizeToStr($size)
{
if (!is_numeric($size) || $size <= 0) {
return '';
}
$size = $size / 1024;
if ($size < 1024) {
return sprintf("%.2fK", $size);
}
$size = $size / 1024;
if ($size < 1024) {
return sprintf("%.2fM", $size);
}
$size = $size / 1024;
return sprintf("%.2fG", $size);
}
}
//根据路径获取文件名
if (!function_exists('getKeyByPath')) {
function getKeyByPath($path)
{
return substr($path, strrpos($path, '/') + 1);
}
}
//富文本中提取图片路径
if (!function_exists('getImageUrlFromText')) {
function getImageUrlFromText($content)
{
preg_match_all('/<img.*?src="(.*?)".*?>/is', $content, $imgs);
$data = [];
if (!empty($imgs) && !empty($imgs[1])) {
foreach ($imgs[1] as $img) {
if (substr($img, 0, 4) != 'http') {
$key = getKeyByPath($img);
$data[$key] = $img;
}
}
}
return $data;
}
}
// 是否http开头
if (!function_exists('isHttpUrl')) {
function isHttpUrl(string $url): bool
{
return substr(trim($url), 0, 4) == 'http';
}
}
//富文本中提取视频路径
if (!function_exists('getVideoUrlFromText')) {
function getVideoUrlFromText($content)
{
preg_match_all('/<video.*?src="(.*?)".*?>/is', $content, $videos);
$data = [];
if (!empty($videos) && !empty($videos[1])) {
foreach ($videos[1] as $video) {
if (substr($video, 0, 4) != 'http') {
$key = getKeyByPath($video);
$data[$key] = $video;
}
}
}
return $data;
}
}
//获取目录下的所有文件
if (!function_exists('getAllFilesByPath')) {
function getAllFilesByPath($path, $rootPath)
{
if (is_dir($path) && file_exists($path)) {
$items = scandir($path);
$files = [];
foreach ($items as $item) {
if (substr($item, 0, 1) != '.' && strpos($item, '_') == false) {
$itemPath = $path.'/'.$item;
if (is_file($itemPath)) {
$size = filesize($itemPath);
$files[$item] = [
'path' => str_replace($rootPath, '/', $itemPath),
'realPath' => $itemPath,
'size' => $size,
'sizeStr' => sizeToStr($size),
'suffix' => strtolower(substr($item, strrpos($item, '.') + 1)) //后缀
];
} elseif (is_dir($itemPath)) {
$childFiles = getAllFilesByPath($itemPath, $rootPath);
if (!empty($childFiles)) {
$files = array_merge($files, $childFiles);
} else {
rmdir($itemPath);
}
}
}
}
return $files;
}
return [];
}
}
//过滤get输入
if (!function_exists('getFilter')) {
function getFilter($value)
{
$getFilter = "'|(and|or)\b.+?(>|<|=|in|like)|\/\*.+?\*\/|<\s*script\b|\bEXEC\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\s+(TABLE|DATABASE)";
$forArray = false;
if (is_array($value)) {
$forFilter = implode($value);
$forArray = true;
} else {
$forFilter = $value;
}
if (preg_match("/".$getFilter."/is", $forFilter) == 1) {
$value = $forArray ? [] : '';
}
return filterExp($value);
}
}
//过滤post录入
if (!function_exists('postFilter')) {
function postFilter($value)
{
$postFilter = "\b(and|or)\b.{1,6}?(=|>|<|\bin\b|\blike\b)|\/\*.+?\*\/|<\s*script\b|\bEXEC\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\s+(TABLE|DATABASE)";
$forArray = false;
if (is_array($value)) {
$forFilter = implode($value);
$forArray = true;
} else {
$forFilter = $value;
}
if (preg_match("/".$postFilter."/is", $forFilter) == 1) {
$value = $forArray ? [] : '';
}
return filterExp($value);
}
}
//过滤cookie数据
if (!function_exists('cookieFilter')) {
function cookieFilter($value)
{
$cookieFilter = "\b(and|or)\b.{1,6}?(=|>|<|\bin\b|\blike\b)|\/\*.+?\*\/|<\s*script\b|\bEXEC\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\s+(TABLE|DATABASE)";
$forArray = false;
if (is_array($value)) {
$forFilter = implode($value);
$forArray = true;
} else {
$forFilter = $value;
}
if (preg_match("/".$cookieFilter."/is", $forFilter) == 1) {
$value = $forArray ? [] : '';
}
return filterExp($value);
}
}
if (!function_exists('filterExp')) {
function filterExp($value)
{
$filter = '/^(\s)+(EXP|NEQ|GT|EGT|LT|ELT|OR|XOR|LIKE|NOTLIKE|NOT LIKE|NOT BETWEEN|NOTBETWEEN|BETWEEN|NOTIN|NOT IN|IN)(\s)+$/i';
$forArray = false;
if (is_array($value)) {
$forFilter = implode($value);
$forArray = true;
} else {
$forFilter = $value;
}
if (preg_match($filter, $forFilter) == 1) {
$value = $forArray ? [] : '';
}
return $value;
}
}
if (!function_exists('arrayHtmlFilter')) {
function arrayHtmlFilter($arr)
{
foreach ($arr as $k => $one) {
if (is_array($one)) {
$arr[$k] = arrayHtmlFilter($one);
} else {
$one = trim($one);
$arr[$k] = strip_tags($one);
}
}
return $arr;
}
}
if (!function_exists('toCamelString')) {
/**
* 转驼峰
*
* @param string $string
* @param false $small 默认false 为true首字母小写
* @return string
*/
function toCamelString(string $string, bool $small = false): string
{
//例: xxx_yYy_zzZ 和 xxX-yyy-Zzz
//1. 字符串转小写 xxx_yyy_zzz xxx-yyy-zzz
//2. -和_转空格 xxx yyy zzz
//3. 单词首字母大写 Xxx Yyy Zzz
//4. 清除空格 XxxYyyZzz
//处理下划线、减号 统统专程大驼峰 如xxx_yyy_zzz xxx-yyy-zzz 均转为 XxxYyyZzz
$string = strtolower($string);
$string = str_replace('-', ' ', $string);
$string = str_replace('_', ' ', $string);
$string = str_replace(' ', '', ucwords($string));
if ($small) {
$string = lcfirst($string);
}
return $string;
}
}
if (!function_exists('unCamelize')) {
/**
* 驼峰命名转特定分隔符[如下划线]命名
* @param string $camelCaps
* @param string $separator
* @return string
*/
function unCamelize(string $camelCaps, string $separator = '-'): string
{
return strtolower(preg_replace('/([a-z])([A-Z])/', "$1".$separator."$2", $camelCaps));
}
}
if (!function_exists('generateRand')) {
/**
* 生成随机数
*
* @param int $length 长度
* @param string $type 模式 默认mix混合 number纯数字 alpha字母
* @return string
*/
function generateRand(int $length = 8, string $type = 'mix'): string
{
$alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$number = '0123456789';
$alphabetLen = strlen($alphabet) - 1;
$numberLen = 9;
switch ($type) {
case 'number':
$str = $number;
$len = $numberLen;
break;
case 'alpha':
$str = $alphabet;
$len = $alphabetLen;
break;
default:
$str = $alphabet.$number;
$len = $alphabetLen + $numberLen;
}
$randStr = '';
$str = str_shuffle($str);
for ($i = 0; $i < $length; $i++) {
$num = mt_rand(0, $len);
$randStr .= $str[$num];
}
return $randStr;
}
}
if (!function_exists('generateCode')) {
/**
* 生成特定编码
*
* @param string $type 类型 默认mix混合 number纯数字 alpha字母
* @param int $len 随机数长度
* @return string
*/
function generateCode(string $type = 'number', int $len = 6): string
{
//#时间戳+微秒+6位随机数
$time = microtime(true);
$timeStr = str_replace('.', '', $time);
return sprintf("%s%s", $timeStr, generateRand($len, $type));
}
}
if (!function_exists('checkMobile')) {
/**
* 检测手机号
*
* @param string $mobile
* @return bool
*/
function checkMobile(string $mobile): bool
{
if (preg_match("/^1[3456789]{1}\d{9}$/", $mobile)) {
return true;
} else {
return false;
}
}
}
if (!function_exists('arrayKeysFilter')) {
/**
* 数组键名过滤
*
* @param array $data
* @param array $allowKeys
* @return array
*/
function arrayKeysFilter(array $data, array $allowKeys): array
{
$list = [];
foreach ($data as $key => $val) {
if (in_array($key, $allowKeys)) {
$list[$key] = $val;
}
}
return $list;
}
}
if (!function_exists('getFilesize')) {
/**
* 尺寸单位转换
*
* @param $num
* @return string
*/
function getFilesize($num): string
{
$p = 0;
$format = 'B';
if ($num > 0 && $num < 1024) {
return number_format($num).' '.$format;
}
if ($num >= 1024 && $num < pow(1024, 2)) {
$p = 1;
$format = 'KB';
}
if ($num >= pow(1024, 2) && $num < pow(1024, 3)) {
$p = 2;
$format = 'MB';
}
if ($num >= pow(1024, 3) && $num < pow(1024, 4)) {
$p = 3;
$format = 'GB';
}
if ($num >= pow(1024, 4) && $num < pow(1024, 5)) {
$p = 3;
$format = 'TB';
}
$num /= pow(1024, $p);
return number_format($num, 3).' '.$format;
}
}
if (!function_exists('arrayNullToString')) {
/**
* 数组|或数据集中null值转为空字符串,并以数组格式返回
* 通常用于api json 返回内容null转换
*
* @param array $data 【array|collection】
* @return array
*/
function arrayNullToString($data)
{
if ($data instanceof Collection || $data instanceof Model) {
$data = $data->toArray();
}
// 判断是否可以遍历
if (is_iterable($data)) {
foreach ($data as $key => $val) {
if ($val instanceof Collection || $data instanceof Model) {
$val = $val->toArray();
}
if (is_iterable($val)) {
$data[$key] = arrayNullToString($val);
} elseif ($val === null) {
$data[$key] = '';
}
}
} else {
$data = [];
}
return $data;
}
}
if (!function_exists('arrayKeysExcludeFilter')) {
/**
* 数组键名排除过滤
*
* @param array $data
* @param array $excludeKeys 排除的字段
* @return array
*/
function arrayKeysExcludeFilter(array $data, array $excludeKeys): array
{
foreach ($data as $key => $val) {
if (in_array($key, $excludeKeys)) {
unset($data[$key]);
}
}
return $data;
}
}
if (!function_exists('getLatelyWeekDate')) {
/**
* 获取本周的周一 到周日的日期
*/
function getLatelyWeekDate()
{
//本周一
$oneDate = date('Y-m-d 00:00:00', (time() - ((date('w') == 0 ? 7 : date('w')) - 1) * 86400)); //w为星期几的数字形式,这里0为周日
$oneDateTime = strtotime($oneDate);
//返回周一到周天 1-7
return [
"1" => ["date" => $oneDate],
"2" => ["date" => date('Y-m-d 00:00:00', $oneDateTime + ((86400 * 1)))],
"3" => ["date" => date('Y-m-d 00:00:00', $oneDateTime + ((86400 * 2)))],
"4" => ["date" => date('Y-m-d 00:00:00', $oneDateTime + ((86400 * 3)))],
"5" => ["date" => date('Y-m-d 00:00:00', $oneDateTime + ((86400 * 4)))],
"6" => ["date" => date('Y-m-d 00:00:00', $oneDateTime + ((86400 * 5)))],
"7" => ["date" => date('Y-m-d 00:00:00', $oneDateTime + ((86400 * 6)))],
];
}
}
if (!function_exists('stringDesensitization')) {
/**
* 字符串脱敏 默认给手机号脱敏 其他未兼容
*
* @param string $string
* @param string $s
* @param int $start
* @param int $len
* @return string
*/
function stringDesensitization(string $string, string $s = '****', int $start = 3, int $len = 4): string
{
return substr_replace($string, $s, $start, $len);
}
}
if (!function_exists('resourceJoin')) {
/**
* 资源拼接前缀 /xxx/xxxx/xxx.jpg 拼接为前缀http://www.xxx.com/xxx/xxxx/xxx.jpg
*
* @param string $string
* @param string $domain
* @return string
*/
function resourceJoin(string $string, string $domain): string
{
if (empty($string)) {
return '';
}
if (isHttpUrl($string)) {
return $string;
}
return $domain.$string;
}
}

41
app/controller/Base.php Normal file
View File

@ -0,0 +1,41 @@
<?php
namespace app\controller;
/**
* 控制器基础类
*/
class Base extends BaseController
{
//需要向模板传递的值
protected $data = [];
//系统配置信息
protected $system = [];
protected $auth = [];
protected $authId = 0;
// 初始化
protected function initialize()
{
$this->auth = session('frontend_auth') ?? [];
$this->data['auth'] = $this->auth;
$this->authId = $this->auth['id'] ?? 0;
}
//设置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);
}
}

View File

@ -0,0 +1,254 @@
<?php
declare (strict_types=1);
namespace app\controller;
use think\{App, Request, response\Json, response\Redirect, response\View, Validate};
use Exception;
use think\exception\ValidateException;
/**
* 控制器基础类
*/
abstract class BaseController
{
/**
* 无需登录的方法,同时也就不需要鉴权了
*
* @var array
*/
protected $noNeedLogin = [];
/**
* 无需鉴权的方法,但需要登录
*
* @var array
*/
protected $noNeedRight = [];
/**
* Request实例
* @var Request
*/
protected $request;
/**
* 应用实例
* @var App
*/
protected $app;
/**
* 是否批量验证
* @var bool
*/
protected $batchValidate = false;
/**
* 控制器中间件
* @var array
*/
protected $middleware = [];
/**
* 构造方法
* @access public
* @param App $app 应用对象
*/
public function __construct(App $app)
{
$this->app = $app;
$this->request = $this->app->request;
// 控制器初始化
$this->initialize();
}
// 初始化
protected function initialize()
{
}
/**
* 验证数据
* @access protected
* @param array $data 数据
* @param string|array $validate 验证器名或者验证规则数组
* @param array $message 提示信息
* @param bool $batch 是否批量验证
* @return array|string|true
* @throws ValidateException
*/
protected function validate(array $data, $validate, array $message = [], bool $batch = false)
{
if (is_array($validate)) {
$v = new Validate();
$v->rule($validate);
} else {
if (strpos($validate, '.')) {
// 支持场景
list($validate, $scene) = explode('.', $validate);
}
$class = false !== strpos($validate, '\\') ? $validate : $this->app->parseClass('validate', $validate);
$v = new $class();
if (!empty($scene)) {
$v->scene($scene);
}
}
$v->message($message);
// 是否批量验证
if ($batch || $this->batchValidate) {
$v->batch(true);
}
return $v->failException(true)->check($data);
}
/**
* 验证器
*
* @param array $data
* @param $validate
* @param array $message
* @param bool $batch
* @return Json|bool
* @throws Exception
*/
protected function validateByApi(array $data, $validate, array $message = [], bool $batch = false)
{
try {
$this->validate($data, $validate, $message, $batch);
return true;
} catch (ValidateException $e) {
$msg = $e->getMessage();
if ($batch) {
$msg = implode(',', $e->getError());
}
return $this->json(4000, $msg);
} catch (Exception $e) {
throw $e;
}
}
/**
* 验证器
*
* @param array $data
* @param $validate
* @param array $message
* @param bool $batch
* @return Redirect
* @throws Exception
*/
protected function validateByView(array $data, $validate, array $message = [], bool $batch = false): Redirect
{
try {
$this->validate($data, $validate, $message, $batch);
} catch (ValidateException $e) {
$msg = $e->getMessage();
if ($batch) {
$msg = implode(',', $e->getError());
}
return $this->error( $msg);
} catch (Exception $e) {
throw $e;
}
}
/**
* 操作成功跳转的快捷方法
* @access protected
* @param mixed $msg 提示信息
* @param string|null $url 跳转的URL地址
* @param mixed $data 返回的数据
* @param integer $wait 跳转等待时间
* @param array $header 发送的Header信息
* @return Redirect
*/
protected function success($msg = '', string $url = null, $data = '', int $wait = 3, array $header = []): Redirect
{
if (is_null($url) && isset($_SERVER["HTTP_REFERER"])) {
$url = $_SERVER["HTTP_REFERER"];
} elseif ($url) {
$url = (strpos($url, '://') || 0 === strpos($url, '/')) ? $url : $this->app->route->buildUrl($url);
}
$result = [
'code' => 1,
'msg' => $msg,
'data' => $data,
'url' => $url,
'wait' => $wait,
];
return $this->redirect(url('error/404', $result));
}
/**
* 操作错误跳转的快捷方法
* @access protected
* @param mixed $msg 提示信息
* @param string|null $url 跳转的URL地址
* @param mixed $data 返回的数据
* @param integer $wait 跳转等待时间
* @return Redirect
*/
protected function error($msg = '', string $url = null, $data = '', int $wait = 3): Redirect
{
if (is_null($url)) {
$referer = $_SERVER['HTTP_REFERER'] ?? null;
if (empty($referer)) {
$url = $this->request->isAjax() ? '' : '/';
} else {
$url = $this->request->isAjax() ? '' : 'javascript:history.back(-1);';
}
} elseif ($url) {
$url = (strpos($url, '://') || 0 === strpos($url, '/')) ? $url : $this->app->route->buildUrl($url);
}
$result = [
'code' => 0,
'msg' => $msg,
'data' => $data,
'url' => $url,
'wait' => $wait,
];
return $this->redirect(url('error/404', $result));
}
/**
* 返回封装后的API数据到客户端
* 以json格式抛出异常
* @access protected
* @param integer $code 返回的code
* @param mixed $msg 提示信息
* @param mixed $data 要返回的数据
* @return Json
*/
protected function json(int $code = 0, $msg = '操作成功', $data = []): Json
{
$result = [
'code' => $code,
'msg' => $msg,
'data' => $data
];
return json($result);
}
/**
* URL重定向
* @access protected
* @param string $url 跳转的URL表达式
* @return Redirect
*/
protected function redirect($url): Redirect
{
if (!is_string($url)) {
$url = $url->__toString();
}
return redirect($url);
}
}

48
app/controller/Error.php Normal file
View File

@ -0,0 +1,48 @@
<?php
namespace app\controller;
class Error extends BaseController
{
public function __call($method, $args)
{
if(request()->isAjax()) {
return $this->json(4004, 'error request!');
} else {
$referer = $_SERVER['HTTP_REFERER'] ?? null;
if (empty($referer)) {
$url = '/';
} else {
$domain = $this->request->domain();
$urlInfo = parse_url($referer);
$scheme = $urlInfo['scheme'] ?? '';
$requestSrc = '';
if (!empty($scheme)) {
$requestSrc = $scheme.'://'.($urlInfo['host'] ?? '');
}
if($domain != $requestSrc) {
$url = '/';
} else {
$url = 'javascript:history.back(-1);';
}
}
$result = [
'code' => 404,
'msg' => '无效请求! 没有找到相关资源',
'data' => [],
'url' => $url,
'wait' => 5,
];
return view('/manager/error/jump')->assign($result);
}
}
public function jump()
{
$param = request()->param();
return view()->assign($param);
}
}

101
app/controller/Index.php Normal file
View File

@ -0,0 +1,101 @@
<?php
namespace app\controller;
use app\repository\OrderRepository;
use app\service\wx\WechatPay;
use Exception;
use think\facade\Log;
use think\response\Redirect;
class Index extends Base
{
/**
* @return Redirect
* @throws Exception
*/
public function index(): Redirect
{
return $this->redirect('/manager');
}
/**
* 退出
*
* @return Redirect
*/
public function logout(): Redirect
{
session('frontend_auth', null);
$req = $this->request->header('referer');
return $this->redirect($req);
}
/**
* 微信的回调
*
* @throws \EasyWeChat\Kernel\Exceptions\Exception
*/
public function callback()
{
if ($this->request->isPost()) {
$app = WechatPay::getInstance();
$response = $app->handlePaidNotify(function ($message, $fail) {
// $aa = '{"appid":"wxa02e44170bc722cd","bank_type":"OTHERS","cash_fee":"1","fee_type":"CNY","is_subscribe":"N","mch_id":"1605090111","nonce_str":"60f7d8a1e4ac8","openid":"oKrEm0ehgsy2ZTWzEva4tbLuUgFw","out_trade_no":"16268555858753004863","result_code":"SUCCESS","return_code":"SUCCESS","sign":"DB3F6CDCB7FBB3B9DDF7C0CC8BBD5AAD","time_end":"20210721162000","total_fee":"1","trade_type":"JSAPI","transaction_id":"4200001200202107217942681078"}';
// $message = json_decode($aa, true);
$m = json_encode($message, JSON_UNESCAPED_UNICODE);
if (!$order = OrderRepository::getInstance()->findOneByWhere(['coding' => $message['out_trade_no']])) {
$this->log(sprintf("[微信支付回调][%s][%s]订单支付成功,但系统查无此订单 info:%s", date('Y-m-d H:i:s'), $message['out_trade_no'], $m), 'error');
return true;//订单不存在
}
//记录日志
$this->log(sprintf("[微信支付回调][%s][%s]订单支付成功 info:%s", date('Y-m-d H:i:s'), $message['out_trade_no'], $m), 'info');
if ($message['return_code'] === 'SUCCESS') { // return_code 表示通信状态,不代表支付状态
//更改订单状态
try {
$res = false;
// 用户是否支付成功
if (isset($message['result_code']) && $message['result_code'] === 'SUCCESS') {
//记录日志
$res = OrderRepository::getInstance()->setPaid($order['coding']);
$this->log(sprintf("[微信支付回调][%s][%s]订单支付成功 修改订单状态为%s", date('Y-m-d H:i:s'), $message['out_trade_no'], $res), 'info');
// 用户支付失败
} elseif (isset($message['result_code']) && $message['result_code'] === 'FAIL') {
//记录日志
$this->log(sprintf("[微信支付回调][%s][%s]订单支付失败 修改订单状态为%s", date('Y-m-d H:i:s'), $message['out_trade_no'], $res), 'info');
}
if (!$res) {
return $fail('Order status edit failed.');
}
} catch (Exception $e) {
$this->log(sprintf("[微信支付回调][%s][%s]订单支付失败 失败原因:%s", date('Y-m-d H:i:s'), $message['out_trade_no'], $e->getMessage()), 'info');
//错误信息 触发
return $fail('Order error.');
}
} else {
return $fail('通信失败,请稍后再通知我');
}
return true;
});
$response->send();
}
}
/**
* 记录订单日志
*
* @param string $message
* @param string $type
*/
private function log(string $message, string $type = 'info'): void
{
Log::channel('order')->write($message, $type);
}
}

284
app/controller/Login.php Normal file
View File

@ -0,0 +1,284 @@
<?php
namespace app\controller;
use app\exception\RepositoryException;
use app\repository\CommonRepository;
use app\validate\Account as VAccount;
use app\repository\AccountRepository;
use Exception;
use Overtrue\Socialite\SocialiteManager;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\facade\Config;
use think\facade\Log;
use think\response\Json;
use think\response\Redirect;
use think\response\View;
class Login extends Base
{
protected $middleware = ['csrf'];
public function index()
{
$referer = input('param.url/s', '/');
$referer = urldecode($referer);
$auth = session('frontend_auth');
if ($auth) {
return $this->redirect($referer);
}
session('login_return', $referer);
if ($this->request->isPost()) {
$username = input('post.username/s');
$password = input('post.password/s');
if (empty($username) || empty($password)) {
return $this->json(4001, '参数错误');
}
$user = AccountRepository::getInstance()->infoByPhone($username);
if (!$user) {
if (!$user = AccountRepository::getInstance()->infoByUsername($username)) {
return $this->json(4002, '账号或密码错误');
}
}
if ($user['password'] !== md5($password)) {
return $this->json(4003, '密码错误若手机验证码方式注册初始密码为手机号后6位');
}
unset($user['password']);
session('frontend_auth', $user->toArray());
$referer = session('login_return') ?? '/';
return $this->json(0, 'success', ['login_return' => $referer]);
}
return $this->view();
}
public function phone()
{
$referer = input('param.url/s', '/');
$referer = urldecode($referer);
$auth = session('frontend_auth');
if ($auth) {
return $this->redirect($referer);
}
session('login_return', $referer);
if ($this->request->isPost()) {
$phone = input('post.phone/s');
$code = input('post.code/s');
if (empty($phone) || empty($code)) {
return $this->json(4001, '参数错误');
}
if (!CommonRepository::getInstance()->checkSms($phone, $code, CommonRepository::SMS_TYPE_LOGIN)) {
return $this->json(4002, '短信验证码错误');
}
$user = AccountRepository::getInstance()->infoByPhone($phone);
if (!$user) {
$password = substr(trim($phone), -6);
$data['mobile'] = $phone;
$data['password'] = md5($password);
$data['nickname'] = trim($phone);
$data['status'] = 'normal';
$data['created_at'] = date('Y-m-d H:i:s');
$user = AccountRepository::getInstance()->create($data);
}
session('frontend_auth', $user);
$referer = session('login_return') ?? '/';
return $this->json(0, 'success', ['login_return' => $referer]);
}
return $this->view();
}
/**
* 微信登录
*
* @return Redirect|View
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
* @throws RepositoryException
*/
public function wechat()
{
$code = input('code/s');
Config::load('extra/wechat', 'wechat');
$wechatConfig = config('wechat');
$referer = session('login_return') ?? '/';
if ($this->auth) {
return $this->redirect($referer);
} else {
if ($code) {
$config = [
'wechat' => [
'client_id' => $wechatConfig['openAppId'],
'client_secret' => $wechatConfig['openAppSecret'],
'redirect' => $wechatConfig['open_notify_url'],
],
];
$socialite = new SocialiteManager($config);
// 已更新版本 具体使用的时候处理
$user = $socialite->driver('wechat')->user();
$wechatUser = $user->getOriginal();
// $wechatUser = [
// 'openid' => 'o05Qy6rt1l7NOjrZsViC2bvS75j0',
// 'nickname' => '拙言',
// 'sex' => '1',
// 'language' => 'language',
// 'city' => 'chengdu ',
// 'province' => '四川',
// 'country' => '中国',
// 'headimgurl' => 'https://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTIZib13lH3Sicc1BO189ibaqohRtXr3OuHw3xSHibn611kaGmmLvppOD5hHyE5IJmicTDpdwCEDw4LF9Xw/132',
// 'unionid' => 'oIrzJv6Vk8s8Hg_rJuNB8muquziw',
// ];
if (empty($wechatUser)) {
}
$account = AccountRepository::getInstance()->findOneByWhere(['unionid' => $wechatUser['unionid']]);
$data = [];
$now = date('Y-m-d H:i:s');
if (!$account) {
//无账号 新建账号
$data = $wechatUser;
$data['created_at'] = $now;
$data['status'] = AccountRepository::STATUS_NORMAL;
$account = AccountRepository::getInstance()->create($wechatUser);
}
$data['login_ip'] = $this->request->ip();
$data['last_login'] = $now;
session('frontend_auth', $account->toArray());
$account->save($data);
if (empty($account['mobile'])) {
//没有手机号 跳转绑定
return $this->redirect('/login/binding');
}
return $this->redirect($referer);
}
}
$openAppId = $wechatConfig['openAppId'] ?? '';
$redirect = $wechatConfig['open_notify_url'] ?? '';
$this->data['redirect'] = urlencode($redirect);
$this->data['openAppId'] = $openAppId;
return $this->view();
}
/**
* 常规注册
*
* @throws Exception
*/
public function register(): Json
{
if ($this->request->isPost()) {
$post = input('post.');
$validate = new VAccount();
if (!$validate->scene('register')->check($post)) {
return $this->json(4001, $validate->getError());
}
if (!CommonRepository::getInstance()->checkSms($post['phone'], $post['code'], CommonRepository::SMS_TYPE_REGISTER)) {
return $this->json(4002, '验证码错误');
}
try {
AccountRepository::getInstance()->registerByNormal($post);
} catch (RepositoryException $e) {
return $this->json(4003, $e->getMessage());
} catch (Exception $e) {
Log::error(sprintf("[注册失败]%s:%s %s", $e->getFile(), $e->getLine(), $e->getMessage()));
return $this->json(5001, '注册失败');
}
return $this->json();
}
}
/**
* 发送注册验证码
*
* @return Json
*/
public function sms(): Json
{
$post = input('post.');
$validate = new VAccount();
if (!$validate->scene('send_sms')->check($post)) {
return $this->json(4001, $validate->getError());
}
if (CommonRepository::getInstance()->sendSms($post['phone'], $post['type'])) {
return $this->json();
}
return $this->json(4002, '验证码发送失败');
}
/**
* 绑定手机号
*
* @return Redirect|Json|View
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
* @throws RepositoryException
*/
public function binding()
{
if ($this->request->isPost()) {
$post = input('post.');
$validate = new VAccount();
if (!$validate->scene('binding')->check($post)) {
return $this->json(4001, $validate->getError());
}
if (!$account = AccountRepository::getInstance()->findById($this->auth['id'] ?? 0)) {
return $this->json(4006, '请先登录');
}
if (!empty($account['mobile'])) {
return $this->json(4007, '您已绑定手机号,无需重复绑定!');
}
if (AccountRepository::getInstance()->infoByPhone($post['phone'])) {
return $this->json(4005, '该手机已绑定账号');
}
if (!CommonRepository::getInstance()->checkSms($post['phone'], $post['code'], CommonRepository::SMS_TYPE_BINDING)) {
return $this->json(4002, '验证码错误');
}
try {
$account->save(['mobile' => $post['phone']]);
} catch (RepositoryException $e) {
return $this->json(4003, $e->getMessage());
} catch (Exception $e) {
CommonRepository::log('绑定手机号失败', $e, 'error');
return $this->json(5001, '绑定手机号失败');
}
return $this->json();
} else {
if (!$account = AccountRepository::getInstance()->findById($this->auth['id'] ?? 0)) {
return $this->redirect('/login');
}
if (!empty($account['mobile'])) {
return $this->redirect('/');
}
}
return $this->view();
}
}

View File

@ -0,0 +1,275 @@
<?php
namespace app\controller\api;
use app\model\AccountRecord;
use app\model\Disease;
use app\repository\ArchivesRepository;
use Exception;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\response\Json;
use app\exception\RepositoryException;
class Archives extends Base
{
protected $noNeedLogin = ['course', 'detail'];
/**
* 病种列表
*
* @return Json
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function disease(): Json
{
$data = Disease::getListByPid();
return $this->json(0, 'success', $data);
}
/**
* 病种问题文章列表 分类问题
*
* @return Json
*/
public function diseaseQuestion(): Json
{
$diseaseId = input('disease_id/d', 0);
try {
$data = ArchivesRepository::getInstance()->diseaseQuestion($diseaseId);
return $this->json(0, 'success', $data);
} catch (RepositoryException $e) {
return $this->json(4001, $e->getMessage());
} catch (Exception $e) {
ArchivesRepository::log($e->getMessage(), $e);
return $this->json(5000, '操作失败');
}
}
/**
* 恒美小课堂
*
* @return Json
* @throws Exception
*/
public function course(): Json
{
$accountId = $this->request->user['user_id'] ?? 0;
$courseId = input('course_id/d', 0);
$page = input('page/d', 1);
$size = input('size/d', 20);
try {
$data = ArchivesRepository::getInstance()->course($accountId, $courseId, $page, $size);
return $this->json(0, 'success', $data);
} catch (RepositoryException $e) {
return $this->json(4001, $e->getMessage());
} catch (Exception $e) {
ArchivesRepository::log($e->getMessage(), $e);
return $this->json(5000, '操作失败');
}
}
/**
* 热门推荐
*
* @return Json
* @throws Exception
*/
public function hot(): Json
{
$categoryId = input('category_id/d', 0);
$page = input('page/d', 1);
$size = input('size/d', 20);
$keyword = input('keyword/s', '');
$accountId = $this->request->user['user_id'] ?? 0;
try {
$where[] = ['hot', '=', 1];
if (!empty($keyword)) {
$keyword = trim($keyword);
$where[] = ['title|subtitle|disease_name|doctor_name', 'like', '%'.$keyword.'%'];
AccountRecord::saveSearch($accountId, $keyword);
}
$order = ['sort' => 'desc'];
$data = ArchivesRepository::getInstance()->category($accountId, $categoryId, $where, $page, $size, $order);
return $this->json(0, 'success', $data);
} catch (RepositoryException $e) {
return $this->json(4001, $e->getMessage());
} catch (Exception $e) {
ArchivesRepository::log($e->getMessage(), $e);
return $this->json(5000, '操作失败');
}
}
/**
* 获取指定栏目内容列表
*
* @return Json
* @throws Exception
*/
public function category(): Json
{
$categoryId = input('category_id/d', 0);
$page = input('page/d', 1);
$size = input('size/d', 20);
$keyword = input('keyword/s', '');
$diseaseIds = input('disease_id/s', '');//病种 多个用逗号分隔
$accountId = $this->request->user['user_id'] ?? 0;
try {
$where = [];
if (!empty($keyword)) {
$keyword = trim($keyword);
$where[] = ['title|subtitle|disease_name|doctor_name', 'like', '%'.$keyword.'%'];
AccountRecord::saveSearch($accountId, $keyword);
}
if (!empty($diseaseIds)) {
$where[] = ['disease_id', 'in', explode(',', $diseaseIds)];
}
$order = ['sort' => 'desc'];
$data = ArchivesRepository::getInstance()->category($accountId, $categoryId, $where, $page, $size, $order);
return $this->json(0, 'success', $data);
} catch (RepositoryException $e) {
return $this->json(4001, $e->getMessage());
} catch (Exception $e) {
ArchivesRepository::log($e->getMessage(), $e);
return $this->json(5000, '操作失败');
}
}
/**
* 获取关于我们栏目内容列表
*
* @return Json
* @throws Exception
*/
public function about(): Json
{
$categoryId = input('category_id/d', 0);
$page = input('page/d', 1);
$size = input('size/d', 20);
$exceptId = input('except_id/d', 0);
$keyword = input('keyword/s', '');
$accountId = $this->request->user['user_id'] ?? 0;
try {
$where = [];
if (!empty($keyword)) {
$keyword = trim($keyword);
$where[] = ['title|subtitle|disease_name|doctor_name', 'like', '%'.$keyword.'%'];
AccountRecord::saveSearch($accountId, $keyword);
}
if ($exceptId > 0) {
$where[] = ['id', '<>', $exceptId];
}
$order = ['published_at' => 'desc'];
$data = ArchivesRepository::getInstance()->about($accountId, $categoryId, $where, $page, $size, $order);
return $this->json(0, 'success', $data);
} catch (RepositoryException $e) {
return $this->json(4001, $e->getMessage());
} catch (Exception $e) {
ArchivesRepository::log($e->getMessage(), $e);
return $this->json(5000, '操作失败');
}
}
/**
* 内容详情
*
* @return Json
* @throws Exception
*/
public function detail(): Json
{
$id = input('id/d', 0);
$shareId = input('share_id/d', 0);//分享人ID
$accountId = $this->request->user['user_id'] ?? 0;
try {
$data = ArchivesRepository::getInstance()->detail($id, $accountId, $shareId);
return $this->json(0, 'success', $data);
} catch (RepositoryException $e) {
return $this->json(4001, $e->getMessage());
} catch (Exception $e) {
ArchivesRepository::log($e->getMessage(), $e);
return $this->json(5000, '获取详情失败');
}
}
/**
* 点赞、收藏
*/
public function record(): Json
{
if (!$this->request->isPost()) {
return $this->json(4000, '无效请求');
}
$accountId = $this->request->user['user_id'] ?? 0;
$archiveId = $this->request->param('archive_id/d', 0);
$action = $this->request->param('action/s', '');
try {
ArchivesRepository::getInstance()->record($accountId, $archiveId, $action);
} catch (RepositoryException $e) {
return $this->json(4001, $e->getMessage());
}
return $this->json();
}
/**
* 取消 点赞、收藏
*/
public function unRecord(): Json
{
if (!$this->request->isPost()) {
return $this->json(4000, '无效请求');
}
$accountId = $this->request->user['user_id'] ?? 0;
$archiveId = $this->request->param('archive_id/d', 0);
$action = $this->request->param('action/s', '');
try {
ArchivesRepository::getInstance()->unRecord($accountId, $archiveId, $action);
} catch (RepositoryException $e) {
return $this->json(4001, $e->getMessage());
}
return $this->json();
}
/**
* 用户内容收藏列表
*/
public function collects(): Json
{
$accountId = $this->request->user['user_id'] ?? 0;
$categoryId = $this->request->param('category_id/d', 0);
$page = $this->request->param('page/d', 1);
$size = $this->request->param('size/d', 10);
$page = $page <= 0 ? 1 : $page;
$size = $size <= 0 ? 10 : $size;
$data = ArchivesRepository::getInstance()->accountCollects($accountId, $categoryId, $page, $size);
return $this->json(0, 'success', $data);
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace app\controller\api;
use app\controller\BaseController;
/**
* API控制器基础类
*/
class Base extends BaseController
{
// 布尔值数字关系
public const BOOL_FALSE = 0;
public const BOOL_TRUE = 1;
protected function initialize()
{
parent::initialize();
$this->middleware = [
'jwt',
'apiLogin' => ['except' => $this->noNeedLogin]
];
}
public function __call($method, $args)
{
return $this->json(4004, 'error request!');
}
}

View File

@ -0,0 +1,91 @@
<?php
namespace app\controller\api;
use app\repository\CommonRepository;
use app\repository\OperationRepository;
use app\repository\OrderRepository;
use app\validate\CommonValidate;
use think\Collection;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\response\Json;
class Common extends Base
{
protected $noNeedLogin = [
'slidePositions',
'slides',
];
/**
* 发送短信验证码
*
* @OA\Post(
* path="/common/send-code",
* tags={"common"},
* operationId="sendCode",
* )
*/
public function sendCode(): Json
{
$input = input('post.');
$validate = new CommonValidate();
if (!$validate->scene('send_sms')->check($input)) {
return $this->json(4001, '参数错误');
}
if (!in_array($input['type'], ['register', 'login', 'binding'])) {
return $this->json(4002, '参数错误');
}
CommonRepository::getInstance()->sendSms($input['phone'], $input['type']);
return $this->json();
}
/**
* 查看轮播图可视位置配置信息
*/
public function slidePositions(): Json
{
$repo = OperationRepository::getInstance();
$list = $repo->slidePositions();
return $this->json(0, 'success', $list);
}
/**
* 轮播图
*/
public function slides(): Json
{
$size = $this->request->param('size/d', 0);
$position = trim($this->request->param('position/s', ''));
if (empty($position)) {
return $this->json();
}
try {
$repo = OperationRepository::getInstance();
$list = $repo->slideListByPosition($position, $size);
} catch (\Exception $e) {
$list = new Collection();
}
return $this->json(0, 'success', $list);
}
/**
* 获取快递公司列表
*
* @throws ModelNotFoundException
* @throws DbException
* @throws DataNotFoundException
*/
public function expressList(): Json
{
$list = OrderRepository::getInstance()->allExpress();
return $this->json(0, 'success', $list);
}
}

View File

@ -0,0 +1,63 @@
<?php
namespace app\controller\api;
use app\exception\RepositoryException;
use app\repository\GoodsRepository;
use app\validate\Goods as GoodsValidate;
use Exception;
use think\response\Json;
/**
* 测试用
*
* Class DepartmentGoodsListLog
* @package app\controller\api
*/
class DepartmentGoodsListLog extends Base
{
/**
* @return Json
* @throws RepositoryException
*/
public function list()
{
$params = $this->request->param();
$validate = new GoodsValidate();
if (!$validate->scene('base')->check($params)) {
return $this->json(4001, $validate->getError());
}
$data = GoodsRepository::getInstance()->list();
return $this->json(0, 'success', $data);
}
/**
* 获取分类列表
*
* @return Json
* @throws Exception
*/
public function category()
{
$params = $this->request->param();
$validate = new GoodsValidate();
if (!$validate->scene('base')->check($params)) {
return $this->json(4001, $validate->getError());
}
$data = GoodsRepository::getInstance()->categoryList();
return $this->json(0, 'success', $data);
}
public function testApiClassMethod()
{
$header = $this->request->header();
$all = $this->request->param();
$get = input('get.');
$post = input('post.');
return $this->json(0, 'success', ['cost'=>$cost ?? 0]);
}
}

View File

@ -0,0 +1,115 @@
<?php
namespace app\controller\api;
use app\exception\RepositoryException;
use app\repository\AccountRepository;
use app\service\ExtraConfig;
use Exception;
use think\facade\Config as CConfig;
use think\response\Json;
class Index extends Base
{
protected $noNeedLogin = [
'miniProgramSetting',
'clearFootmarks',
'baseConfig',
'work',
'kf',
'hotKeywords',
'statement',
'about'
];
public function index(): Json
{
return json(['code' => 0, 'msg' => 'I am index']);
}
/**
* 测试用
*
* @return Json
* @throws RepositoryException
*/
public function test(): Json
{
$userId = $this->request->middleware('userInfo')['user_id'] ?? 0;
$user = AccountRepository::getInstance()->findById($userId, []);
return json(['code' => 0, 'msg' => 'I am test ', 'data' => $user]);
}
public function login(): Json
{
$userId = $this->request->middleware('userInfo')['user_id'] ?? 0;
return json(['code' => 0, 'msg' => 'I am login '.$userId]);
}
public function notify()
{
$beginNotifyList = AccountRepository::getInstance()->getBeginNotifyList();
// $res = Queue::later(3, NotifySms::class, $beginNotifyList);
// $getSuccessList = AccountRepository::getInstance()->getSuccessList();
return $this->json(0, 'success', $beginNotifyList);
}
/**
* 小程序个性装修配置
*/
public function miniProgramSetting(): Json
{
$conf = ExtraConfig::miniProgram();
return $this->json(0, 'success', $conf);
}
/**
* 基础配置
*
* @return Json
*/
public function baseConfig(): Json
{
try {
CConfig::load('extra/base', 'base');
$res = config('base')['show_video'] ?? 0;
return $this->json(0, 'success', ['v' => (int) $res]);
} catch (Exception $e) {
return $this->json(5000, '获取基础配置失败');
}
}
/**
* 免责声明
*
* @return Json
*/
public function statement(): Json
{
try {
CConfig::load('extra/statement', 'statement');
$content = config('statement')['content'] ?? '';
return $this->json(0, 'success', ['content' => $content]);
} catch (Exception $e) {
return $this->json(5000, '获取免责声明失败');
}
}
/**
* 关于我们
*
* @return Json
*/
public function about(): Json
{
try {
CConfig::load('extra/about', 'about');
$res = config('about')?? [];
return $this->json(0, 'success', $res);
} catch (Exception $e) {
return $this->json(5000, '获取关于我们失败');
}
}
}

View File

@ -0,0 +1,430 @@
<?php
namespace app\controller\api;
use app\exception\RepositoryException;
use app\repository\OrderRepository;
use app\model\Order as OrderModel;
use Exception;
use GuzzleHttp\Exception\GuzzleException;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\response\Json;
/**
* 订单
*
* Class Order
* @package app\controller\api
*/
class Order extends Base
{
/**
* 创建订单
*
* @return Json
* @throws GuzzleException
*/
public function create(): Json
{
if ($this->request->isPost()) {
$params = $this->request->param();
$accountId = $this->request->user['user_id'] ?? 0;
try {
$data = OrderRepository::getInstance()->createOrder($accountId, $params);
OrderRepository::getInstance()->updateSpuStock([]);
return $this->json(0, 'success', $data);
} catch (RepositoryException $e) {
return $this->json(4000, $e->getMessage());
} catch (Exception $e) {
OrderRepository::log('订单创建失败', $e, 'error', 'order');
return $this->json(5000, '订单创建失败');
}
}
return $this->json(4002, '请求错误');
}
/**
* 购物车列表
*
* @return Json
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function shoppingCart(): Json
{
$params = $this->request->param();
$accountId = $this->request->user['user_id'] ?? 0;
$type = $params['type'] ?? '';
$page = $params['page'] ?? 1;
$size = $params['size'] ?? 20;
$domain = $this->request->domain();
$data = OrderRepository::getInstance()->shoppingCart($accountId, $type, $page, $size);
$data['list'] = $data['list']->each(function ($item) use ($domain) {
$item->spu->spu_cover = $item->spu->spu_cover ?? '';
$item->spu->spu_cover = resourceJoin($item->spu->spu_cover, $domain);
});
return $this->json(0, 'success', $data);
}
/**
* 添加购物车
*
* @return Json
* @throws Exception
*/
public function shoppingCartAdd(): Json
{
if ($this->request->isPost()) {
$params = $this->request->param();
$accountId = $this->request->user['user_id'] ?? 0;
$rules = [
'sku_id|商品' => 'require|number',
'num|数量' => 'require|number',
];
$validate = $this->validateByApi($params, $rules);
if ($validate !== true) {
return $validate;
}
try {
OrderRepository::getInstance()->shoppingCartAdd($accountId, $params['sku_id'], $params['num'] ?? 1);
return $this->json();
} catch (RepositoryException $e) {
return $this->json(4000, $e->getMessage());
} catch (Exception $e) {
OrderRepository::log('购物车添加失败', $e);
return $this->json(5000, '购物车添加失败');
}
}
return $this->json(4002, '请求错误');
}
/**
* 购物车商品数量变更
*
* @return Json
* @throws Exception
*/
public function shoppingCartChangeNum(): Json
{
if ($this->request->isPost()) {
$params = $this->request->param();
$rules = [
'id|ID' => 'require|number',
'num|数量' => 'require|number',
];
$validate = $this->validateByApi($params, $rules);
if ($validate !== true) {
return $validate;
}
try {
OrderRepository::getInstance()->shoppingCartChangeNum($params['id'], $params['num']);
} catch (RepositoryException $e) {
return $this->json(4000, $e->getMessage());
} catch (Exception $e) {
OrderRepository::log('购物车数量加减失败', $e);
return $this->json(5000, '操作失败');
}
return $this->json();
}
return $this->json(4002, '请求错误');
}
/**
* 购物车商品删除
*
* @return Json
*/
public function shoppingCartDel(): Json
{
if ($this->request->isPost()) {
$id = input('post.id/d', 0);
$accountId = $this->request->user['user_id'] ?? 0;
if (!$id) {
return $this->json(4001, '参数错误');
}
try {
OrderRepository::getInstance()->shoppingCartDel($accountId, $id);
} catch (RepositoryException $e) {
return $this->json(4000, $e->getMessage());
} catch (Exception $e) {
OrderRepository::log('购物车数量加减失败', $e);
return $this->json(5000, '操作失败');
}
return $this->json();
}
return $this->json(4002, '请求错误');
}
/**
* 订单准备信息
* 结算页面 获取商品数据及汇总金额
* @return Json
*/
public function prepareInfo(): Json
{
if ($this->request->isPost()) {
$params = $this->request->param();
$accountId = $this->request->user['user_id'] ?? 0;
$params['domain'] = $this->request->domain();
try {
$data = OrderRepository::getInstance()->prepareInfo($accountId, $params);
} catch (RepositoryException $e) {
return $this->json(4000, $e->getMessage());
} catch (Exception $e) {
OrderRepository::log('获取订单前置信息失败', $e, 'error', 'order');
return $this->json(5000, '订单信息获取失败');
}
return $this->json(0, 'success', $data);
}
return $this->json(4002, '请求错误');
}
/**
* 支付成功通知
* 结算页面 获取商品数据及汇总金额
*
* @return Json
* @throws Exception
*/
public function paid(): Json
{
if ($this->request->isPost()) {
$params = $this->request->param();
$accountId = $this->request->user['user_id'] ?? 0;
$rules = [
'order_coding|订单编号' => 'require',
];
$validate = $this->validateByApi($params, $rules);
if ($validate !== true) {
return $validate;
}
try {
if (OrderRepository::getInstance()->setPaid($params['order_coding'])) {
return $this->json();
}
return $this->json(4003, '支付失败');
} catch (RepositoryException $e) {
return $this->json(4000, $e->getMessage());
} catch (Exception $e) {
OrderRepository::log('支付成功通知操作失败', $e);
return $this->json(5000, '操作失败');
}
}
return $this->json(4002, '请求错误');
}
/**
* 订单验收 - 确认收货
*
* @return Json
*/
public function accepted(): Json
{
if (!$this->request->isPost()) {
return $this->json(4002, '请求错误');
}
$accountId = $this->request->user['user_id'] ?? 0;
$orderId = $this->request->param('order_id', 0);
try {
OrderRepository::getInstance()->orderAccepted($orderId, $accountId);
} catch (RepositoryException $e) {
return $this->json(4000, $e->getMessage());
}
return $this->json();
}
/**
* 发货
*
* @return Json
*/
public function ship(): Json
{
if (!$this->request->isPost()) {
return $this->json(4002, '请求错误');
}
$orderId = $this->request->param('order_id', 0);
$expressId = $this->request->param('express_id', 0);
$expressNumber = $this->request->param('express_number', 0);
try {
OrderRepository::getInstance()->orderShipping($orderId, $expressId, $expressNumber);
} catch (RepositoryException $e) {
return $this->json(4000, $e->getMessage());
}
return $this->json();
}
/**
* 查询订单物流
*
* @return Json
*/
public function logistics(): Json
{
$orderCoding = input('order_coding');
try {
$res = OrderRepository::getInstance()->logistics($orderCoding);
return $this->json(0, 'success', $res);
} catch (RepositoryException $e) {
return $this->json(4000, $e->getMessage());
} catch (Exception $e) {
OrderRepository::log('物流查询失败', $e);
return $this->json(5000, '获取物流信息失败');
}
}
/**
* 取消订单
*
* @return Json
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function cancel(): Json
{
if (!$this->request->isPost()) {
return $this->json(4002, '请求错误');
}
$orderCoding = $this->request->param('order_coding', 0);
$reason = $this->request->param('remarks', '');
try {
OrderRepository::getInstance()->setClosed($orderCoding, OrderModel::STATUS_CLOSED, $reason);
} catch (RepositoryException $e) {
return $this->json(4000, $e->getMessage());
}
return $this->json();
}
/**
* 订单付款
*
* @return Json
* @throws Exception
*/
public function pay(): Json
{
if (!$this->request->isPost()) {
return $this->json(4002, '请求错误');
}
$accountId = $this->request->user['user_id'] ?? 0;
$orderCoding = $this->request->param('order_coding', 0);
try {
$res = OrderRepository::getInstance()->pay($orderCoding, $accountId);
return $this->json(0, 'success', $res);
} catch (RepositoryException $e) {
return $this->json(4000, $e->getMessage());
}
}
/**
* 购物车数量
*
* @return Json
* @throws Exception
*/
public function shoppingCartCount(): Json
{
$accountId = $this->request->user['user_id'] ?? 0;
$type = $this->request->param('type/s', 'spu');
try {
$count = OrderRepository::getInstance()->shoppingCartCount($accountId, $type);
return $this->json(0, 'success', ['count' => $count]);
} catch (RepositoryException $e) {
return $this->json(4000, $e->getMessage());
}
}
/**
* 商品sku核验
*
*/
public function check(): Json
{
$orderCoding = input('order_coding/s', '');
$id = input('id/d', 0);
$checkBy = input('check_user/d', 0);
$num = input('num/d', 1);
try {
OrderRepository::getInstance()->check($orderCoding, $id, $num, $checkBy);
// 核验后事件
event('OrderSpuCheck', $orderCoding);
} catch (RepositoryException $e) {
return $this->json(4000, $e->getMessage());
} catch (Exception $e) {
OrderRepository::log('订单商品核验失败', $e);
return $this->json(5000, '订单商品核验失败');
}
return $this->json();
}
/**
* 商品sku核验结果
*
*/
public function checkResult(): Json
{
$orderCoding = input('order_coding/s', '');
$id = input('id/d', 0);
$notCheckNum = input('not_check_num/d', 0);
try {
$res = OrderRepository::getInstance()->checkResult($orderCoding, $id, $notCheckNum);
return $this->json(0, 'success', ['result' => (int) $res]);
} catch (RepositoryException $e) {
return $this->json(4000, $e->getMessage());
} catch (Exception $e) {
OrderRepository::log('订单商品核验结果获取失败', $e);
return $this->json(5000, '订单商品核验结果获取失败');
}
}
/**
* 获取拼团ID
*
* @return Json
*/
public function getGroupId(): Json
{
$params = $this->request->param();
$orderCoding = $params['order_coding'] ?? '';
$groupId = OrderRepository::getInstance()->groupId($orderCoding);
return $this->json(0, 'success', ['group_id' => $groupId]);
}
}

179
app/controller/api/Sign.php Normal file
View File

@ -0,0 +1,179 @@
<?php
namespace app\controller\api;
use app\exception\RepositoryException;
use app\model\Account;
use app\repository\AccountRepository;
use think\Exception;
use think\facade\Config;
use think\facade\Db;
use think\response\Json;
class Sign extends Base
{
protected $noNeedLogin = [];
/**
* 签到页面加载
* */
public function miniLoad()
{
Config::load('extra/base', 'base');
$baseConfig = config('base');
$score = isset($baseConfig['sign_score']) ? abs($baseConfig['sign_score']) : 1;
$userId = $this->request->user['user_id'] ?? 0;
if ($userId == 0) {
return $this->json(6001, "请先登录");
}
$account = Account::findOne(["id" => $userId], []);
if (empty($account)) {
return $this->json(6001, "请先登录");
}
//更新连续签到次数
AccountRepository::getInstance()->checkContinuitySign($account);
$weekDate = getLatelyWeekDate();
$weedSignInOnlineRecord = AccountRepository::getInstance()->weedSignInOnlineRecord($userId, $weekDate["1"]["date"], strtotime($weekDate["7"]["date"]) + 86399);
foreach ($weedSignInOnlineRecord as $item) {
$w = date("w", strtotime($item['created_at']));
if ($w == 0) {
$weekDate["1"]["record"] = $item["score"];
} else {
$weekDate[$w]["record"] = $item["score"];
}
$weekDate[$w]['is_sign'] = AccountSignOnline::COMMON_ON;//当天是否签到
}
$todaySignIn = AccountSignOnline::COMMON_OFF;
foreach ($weekDate as &$item) {
$key = date("m.d", strtotime($item['date']));
if (!isset($item['record'])) {
$item['record'] = $score;
$item['is_sign'] = AccountSignOnline::COMMON_OFF;//当天是否签到
}
if ($key == date("m.d")) {
$key = "今天";
if ($item['is_sign'] == AccountSignOnline::COMMON_ON) {
$todaySignIn = AccountSignOnline::COMMON_ON;
}
} elseif ($key == (date("m.d", strtotime("+1 day")))) {
$key = "明天";
}
$item["key"] = $key;
}
return $this->json(0, "操作成功", [
"sign_record" => $weekDate,
"reward_score" => $score,
"user_score" => $account['score'],
"today_sign_in" => $todaySignIn,
]);
}
/**
* 签到记录
* */
public function onlineSignRecord()
{
$userId = $this->request->user['user_id'] ?? 0;
if ($userId == 0) {
return $this->json(6001, "请先登录");
}
$page = input("page/d", 1);
$size = input("size/d", 10);
$record = AccountRepository::getInstance()->onlineSignRecordList($userId, $page, $size);
if ($record->isEmpty()) {
return $this->json(4001, "没有更多");
}
return $this->json(0, "ok", $record);
}
/**
* 线上签到
* */
public function onlineSingIn(): Json
{
//检查是否已经签到
$userId = $this->request->user['user_id'] ?? 0;
if ($userId == 0) {
return $this->json(6001, "请先登录");
}
$check = AccountRepository::getInstance()->checkSignInOnline($userId);
if ($check) {
return $this->json(4003, "今天已经签到了,请明天再来");
}
$account = Account::findOne(["id" => $userId], [], function ($q) {
return $q->lock(true);
});
if (empty($account)) {
return $this->json(6001, "请先登录");
}
//更新连续签到次数
AccountRepository::getInstance()->checkContinuitySign($account);
Config::load('extra/base', 'base');
$baseConfig = config('base');
$score = isset($baseConfig['sign_score']) ? abs($baseConfig['sign_score']) : 1;
Db::startTrans();
try {
AccountRepository::getInstance()->SignInOnline($account, $score);
Db::commit();
return $this->json();
} catch (Exception $e) {
Db::rollback();
$this->json(4003, "签到失败");
} catch (RepositoryException $e) {
Db::rollback();
$this->json(4003, "签到失败");
}
}
/**
* 线下签到
* */
public function SingIn(): Json
{
//检查是否已经签到
$userId = $this->request->user['user_id'] ?? 0;
if ($userId == 0) {
return $this->json(6001, "请先登录");
}
$check = AccountRepository::getInstance()->checkSignIn($userId);
if ($check) {
return $this->json(4003, "今天已经签到了,请明天再来");
}
Db::startTrans();
try {
AccountRepository::getInstance()->SignIn($userId);
Db::commit();
return $this->json();
} catch (RepositoryException $e) {
Db::rollback();
$this->json(4003, "签到失败");
} catch (Exception $e) {
Db::rollback();
$this->json(5000, "签到失败");
}
}
}

385
app/controller/api/Spu.php Normal file
View File

@ -0,0 +1,385 @@
<?php
namespace app\controller\api;
use app\controller\manager\mall\Category;
use app\exception\RepositoryException;
use app\model\AccountRecord;
use app\model\Spu as SpuModel;
use app\model\SpuActivity;
use app\repository\OrderRepository;
use app\repository\SpuRepository;
use Exception;
use think\Collection;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\facade\Log;
use think\response\Json;
class Spu extends Base
{
protected $noNeedLogin = ['list', 'category', 'condition', 'detail', 'home'];
/**
* 商品列表筛选条件
*
*/
public function category(): Json
{
$list = SpuRepository::getInstance()->category()->toArray();
return $this->json(0, 'success', $list);
}
/**
* 商品列表筛选条件
*
*/
public function condition(): Json
{
$list = [
[
'title' => '分类',
'field' => 'category_id',
'children' => array_merge([['name' => '全部', 'value' => '']], SpuRepository::getInstance()->category(0, ['id as value', 'title as name'])->toArray())
],
[
'title' => '活动',
'field' => 'activity',
'children' => array_merge([['name' => '全部', 'value' => '']], SpuModel::activity())
]
];
return $this->json(0, 'success', $list);
}
/**
* 首页商品列表
*
* @return Json
* @throws RepositoryException
* @throws Exception
*/
public function home(): Json
{
$repo = SpuRepository::getInstance();
$fields = [
'id', 'name', 'subtitle', 'price', 'original_price', 'cover', 'home_display', 'stock', 'amount','activity_id','activity_type'
];
$params = input();
$params['fields'] = $fields;
$params['is_home'] = SpuModel::COMMON_ON;
$params['is_score'] = SpuModel::COMMON_OFF;//排除积分商品
if (!isset($params['category_id']) || empty($params['category_id'])) {
$params['category_id'] = \app\model\mall\Category::getFirst();
}
$list = $repo->listForFront($params, function ($q) {
return $q->withAttr('cover', function ($value, $data) {
return resourceJoin($value, $this->request->domain());
});
});
return $this->json(0, 'success', $list);
}
/**
* 获取已发布的商品列表
*
* @return Json
* @throws RepositoryException
* @throws Exception
*/
public function list(): Json
{
$repo = SpuRepository::getInstance();
$fields = SpuModel::spuListFields();
$params = input();
$params['fields'] = $fields;
$params['is_score'] = SpuModel::COMMON_OFF;//排除积分商品
$list = $repo->listForFront($params, function ($q) {
return $q->withAttr('cover', function ($value, $data) {
return resourceJoin($value, $this->request->domain());
});
});
return $this->json(0, 'success', $list);
}
/**
* 获取已发布的积分商品列表
*
* @return Json
* @throws RepositoryException
* @throws Exception
*/
public function score(): Json
{
$repo = SpuRepository::getInstance();
$type = input('type/s', SpuModel::TYPE_NORMAL);//normal=综合 newest=最新
$sortField = input('sort_field/s', '');// score=积分 num=兑换量
$sortValue = input('sort_value/s', '');//desc=降序 asc=升序
$rules = [
'page|页数' => 'integer|gt:0',
'size|每页数量' => 'integer|gt:0',
'type|类型' => 'in:newest,'.SpuModel::TYPE_NORMAL,
'sort_field|排序字段' => 'in:score,amount',
'sort_value|排序值' => 'in:asc,desc',
];
$message = [
'type.in' => '类型错误',
'$sortField.in' => '排序字段错误',
'sort_value.in' => '排序值错误',
];
$params = input();
$validate = $this->validateByApi($params, $rules, $message);
if ($validate !== true) {
return $validate;
}
$order = [];//排序
// 综合排序
if ($type === SpuModel::TYPE_NORMAL) {
$order = [
'sort' => 'desc',
'id' => 'desc',
];
}
// 最新排序
if ($type === 'newest') {
$order = ['published_at' => 'desc'];
}
// 兑换量排序
if (!empty($sortField)) {
if (empty($sortValue)) {
return $this->json(4003, '排序参数错误');
}
$order = [
$sortField => $sortValue
];
}
$params['is_score'] = SpuModel::COMMON_ON;
$params['fields'] = SpuModel::scoreListFields();
$list = $repo->listForFront($params, function ($q) {
return $q->withAttr('cover', function ($value, $data) {
return resourceJoin($value, $this->request->domain());
});
}, $order);
return $this->json(0, 'success', $list);
}
/**
* 收藏列表
*
* @return Json
* @throws Exception
*/
public function collection(): Json
{
$rules = [
'page|页数' => 'integer',
'size|每页数量' => 'integer',
];
$params = input();
$page = $params['page'] ?? 1;
$size = $params['size'] ?? 10;
$accountId = $this->request->user['user_id'] ?? 0;
$params['page'] = 1;
$params['size'] = 0;
$validate = $this->validateByApi($params, $rules);
if ($validate !== true) {
return $validate;
}
//获取收藏相关
$collection = AccountRecord::where('type', AccountRecord::TYPE_SPU)
->where('action', AccountRecord::ACTION_COLLECT)
->where('account_id', $accountId)
->where('is_record', AccountRecord::COMMON_ON)
->order('recorded_at', 'desc');
$total = $collection->count();
if ($total <= 0) {
return $this->json(0, 'success', [
'total' => 0,
'current' => $page,
'size' => $size,
'list' => new Collection(),
]);
}
$recordList = $collection->page($page)->limit($size)->field('relation_id,recorded_at')->select();
$where = [];
$where[] = ['id', 'in', $recordList->column('relation_id')];
$list = SpuRepository::getInstance()->listForFront($params, function ($q) {
return $q->withAttr('cover', function ($value, $data) {
return resourceJoin($value, $this->request->domain());
});
}, [], $where);
$data = [];
$spuList = $list['list']->toArray();
foreach ($recordList as $record) {
foreach ($spuList as $key => $spu) {
if ($record['relation_id'] == $spu['id']) {
$data[] = $spu;
unset($spuList[$key]);
}
}
}
$list['total'] = $total;
$list['current'] = $page;
$list['size'] = $size;
$list['list'] = $data;
return $this->json(0, 'success', $list);
}
/**
* SPU 详情
*/
public function detail(): Json
{
$repo = SpuRepository::getInstance();
$id = input('id/d', 0);
$accountId = $this->request->user['user_id'] ?? 0;
$domain = $this->request->domain();
try {
$data = $repo->detail($id, $accountId);
$data['detail']['cover'] = $data['detail']['cover'] ?? '';
$data['detail']['cover'] = resourceJoin($data['detail']['cover'], $domain);
$data['detail']['share_img'] = $data['detail']['share_img'] ?? '';
$data['detail']['share_img'] = resourceJoin($data['detail']['share_img'], $domain);
if(isset($data['detail']['images']) && !empty($data['detail']['images'])){
$images = explode(',', $data['detail']['images']);
$imageArr = [];
foreach ($images as $image) {
if(!empty($image)){
$imageArr[] = resourceJoin($image, $domain);
}
}
$data['detail']['images'] = implode(',', $imageArr);
}
return $this->json(0, 'success', $data);
} catch (RepositoryException $e) {
return $this->json(4001, $e->getMessage());
} catch (Exception $e) {
$repo->log($e->getMessage(), $e);
return $this->json(5000, '获取详情失败');
}
}
/**
* 收藏
*/
public function record(): Json
{
if (!$this->request->isPost()) {
return $this->json(4000, '无效请求');
}
$accountId = $this->request->user['user_id'] ?? 0;
$id = $this->request->param('id/d', 0);
$action = $this->request->param('action/s', '');
try {
if ($accountId <= 0 || $id <= 0) {
return $this->json(4001, '无效请求');
}
if (!in_array($action, AccountRecord::allowActions())) {
return $this->json(4001, '操作类型参数错误');
}
if (!SpuModel::findById($id)) {
return $this->json(4001, '商品不存在');
}
AccountRecord::record($accountId, AccountRecord::TYPE_SPU, $action, $id);
} catch (Exception $e) {
Log::error('[商品记录失败]'.$e->getMessage());
return $this->json(5000, '操作失败');
}
return $this->json();
}
/**
* 取消 收藏
*/
public function unRecord(): Json
{
if (!$this->request->isPost()) {
return $this->json(4000, '无效请求');
}
$accountId = $this->request->user['user_id'] ?? 0;
$id = $this->request->param('id/d', 0);
$action = $this->request->param('action/s', '');
try {
if ($accountId <= 0 || $id <= 0) {
return $this->json(4001, '无效请求');
}
if (!in_array($action, AccountRecord::allowActions())) {
return $this->json(4001, '操作类型参数错误');
}
if (!SpuModel::findById($id)) {
return $this->json(4001, '商品不存在');
}
AccountRecord::unRecord($accountId, $id, AccountRecord::TYPE_SPU, $action);
} catch (Exception $e) {
Log::error('[取消商品记录失败]'.$e->getMessage());
return $this->json(5000, '操作失败');
}
return $this->json();
}
/**
* 获取商品规格信息
*
* @return Json
* @throws RepositoryException
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function spec(): Json
{
$spuId = input('id');
return $this->json(0, 'success', SpuRepository::getInstance()->getSpec($spuId));
}
}

View File

@ -0,0 +1,77 @@
<?php
namespace app\controller\api;
use app\model\AccountFootmarks;
use app\model\Event;
use Exception;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\response\Json;
class Statistics extends Base
{
protected $noNeedLogin = ['event'];
/**
* 事件列表
*
* @return Json
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function event(): Json
{
$data = Event::field('id, name')->select()->toArray();
return $this->json(0, 'success', $data);
}
/**
* 上报
*
* @return Json
* @throws Exception
*/
public function report(): Json
{
$data = input();
$accountId = $this->request->user['user_id'] ?? 0;
if (empty($data)) {
return $this->json();
}
if (!is_array($data)) {
$data = json_decode($data, true);
}
$insert = [];
if (count($data) > 1000) {
//TODO 太大就分片处理
} else {
foreach ($data as $d) {
if (!isset($d['e']) || !isset($d['t'])) {
return $this->json(4001, '参数错误');
}
$arr = [];
$arr['event_id'] = (int) ($d['e'] ?? 0);
$arr['account_id'] = $accountId;
$arr['content_id'] = (int) ($d['c'] ?? 0);
$t = (strlen($d['t']) == 13) ? $d['t'] / 1000 : $d['t'];
$arr['created_at'] = date('Y-m-d H:i:s', $t);
$insert[] = $arr;
}
}
// 若量大 时间长 可丢入队列操作
if (count($insert) > 0) {
(new AccountFootmarks())->saveAll($insert);
}
return $this->json();
}
}

1215
app/controller/api/User.php Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,132 @@
<?php
namespace app\controller\api\file;
use app\controller\api\Base;
use app\model\File;
use app\model\System;
use app\service\Image;
use app\validate\Upload as VUpload;
use think\facade\Config;
use think\facade\Filesystem;
use think\facade\Lang;
use think\response\Json;
/**
* 文件上传
*
* Class Upload
* @package app\controller\api\file
*/
class Upload extends Base
{
protected $noNeedLogin = [];
// 图片上传是否进行压缩[max-width:1920px]
private bool $isCompress = true;
private $validate = null;
// 文件上传对外默认保存目录(相对路径)
private string $uploadPath = '';
// 文件上传对外默认保存目录是否有写权限
private bool $uploadPathIsWritable = false;
protected bool $saveToOos = false;
public function initialize()
{
parent::initialize();
$system = System::getSystem();
if (!empty($system)) {
$this->isCompress = $system['compress'] ?? true;
}
$this->validate = new VUpload();
$this->uploadPath = Config::get('filesystem.disks.local.url');
if(is_writable(app()->getRootPath() . 'public' . $this->uploadPath)){
$this->uploadPathIsWritable = true;
}
$this->cancelTimeLimit();
}
/**
* 通用文件上传
* @return Json
*/
public function file()
{
$file = request()->file('file');
if (empty($file)) {
return $this->json(4001, '请上传的文件');
}
if($this->validate->checkFile($file)){
try{
if(!$this->uploadPathIsWritable){
throw new \Exception('上传文件夹需要写入权限');
}
$src = Filesystem::putFile('files/'.date('Ym'), $file, 'uniqid');
$src = $this->uploadPath . '/' . $src;
$return['src'] = $src;
$return['name'] = $file->getOriginalName();
//加入上传文件表
File::add($file, $src, $file->md5());
} catch (\Exception $e) {
return $this->json(4003, $e->getMessage());
}
return $this->json(0,'success', $return);
}else{
$errorMsg = Lang::get($this->validate->getError());
return $this->json(4002, $errorMsg);
}
}
/**
* 通用图片上传
* @return Json
*/
public function image()
{
$image = request()->file('image');
if (empty($image)) {
return $this->json(4001, '请上传图片文件');
}
$md5 = $image->md5();//文件md5
if($this->validate->checkImage($image)){
try{
if(!$this->uploadPathIsWritable){
throw new \Exception('上传文件夹需要写入权限');
}
$src = Filesystem::putFile('images/'.date('Ym'), $image, 'uniqid');
$src = $this->uploadPath . '/' . $src;
$return['src'] = $src;
if($this->isCompress){
Image::resize($src);
}
//加入上传文件表
File::add($image, $src,$md5);
} catch (\Exception $e) {
return $this->json(4003, $e->getMessage());
}
return $this->json(0, 'success', $return);
}else{
$errorMsg = Lang::get($this->validate->getError());
return $this->json(4002, $errorMsg);
}
}
/**
* 同步到OOS服务器存储
* @param string $src
*/
private function syncToOos(string $src)
{
}
}

View File

@ -0,0 +1,353 @@
<?php
namespace app\controller\manager;
use app\model\Log;
use app\model\Archives as ArchivesModel;
use app\model\ArchivesCategory as ArticleCategoryModel;
use app\model\ArchivesModelField;
use app\model\Config;
use app\repository\CmsRepository;
use Exception;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\exception\ValidateException;
use think\facade\Db;
use think\response\Json;
use think\response\View;
/**
* 档案管理|内容管理
*
* Class Archives
* @package app\controller\manager
*/
class Archives extends Base
{
protected $noNeedLogin = [];
/**
* 删除
*
* @return Json
*/
public function del(): Json
{
if ($this->request->isPost()) {
$ids = input('post.ids/a', []);
if (empty($ids)) {
$ids[] = input('post.id/d');
}
ArchivesModel::deleteByIds($ids);
// 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 = 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['showList'] = json_encode($showFieldList, JSON_UNESCAPED_UNICODE);
$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['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('top', 'desc')
->order('recommend', 'desc')
->order('hot', 'desc')
->order('sort', 'desc')
->order('id', 'desc');
});
$diseaseCategoryIds = ArticleCategoryModel::diseaseCategoryIds();
$items['list'] = $items['list']->each(function ($item) use ($diseaseCategoryIds) {
$item->mpPath = in_array($item['category_id'], $diseaseCategoryIds) ? Config::MINI_PATH_PROBLEM : Config::MINI_PATH_ARCHIVES;
});
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,model_id')
->select();
$modelList = \app\model\ArchivesModel::column('title', 'id');
$category->each(function ($item) use ($modelList) {
$item->title = sprintf("%s-[所属模型:%s]", $item->title, $modelList[$item->model_id]);
});
$category = $category->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);
}
/**
* 批量审核
*
* @return View|Json
* @throws Exception
*/
public function check()
{
$id = input('id/s', '');
if ($this->request->isPost()) {
$ids = input('ids/s');
$check = input('is_check/d');
if (!in_array($check, [ArchivesModel::COMMON_ON, ArchivesModel::COMMON_OFF])) {
return $this->json(4001, '请选择是否展示');
}
$ids = explode(',', $ids);
Db::startTrans();
try {
(new ArchivesModel())->whereIn('id', $ids)->save(['is_check' => $check]);
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();
}
}

View File

@ -0,0 +1,251 @@
<?php
namespace app\controller\manager;
use app\repository\CmsRepository;
use app\model\Log;
use app\model\ArchivesCategory as ArchivesCategoryModel;
use app\validate\MenuValidate;
use Exception;
use think\facade\Db;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\exception\ValidateException;
use think\response\Json;
use think\response\View;
/**
* 栏目管理
*
* Class Menu
* @package app\controller\manager
*/
class ArchivesCategory extends Base
{
/**
* 删除
*
* @return Json
*/
public function del(): Json
{
if ($this->request->isPost()) {
$ids = input('post.ids/a', []);
if (empty($ids)) {
$ids[] = input('post.id/d');
}
if (ArchivesCategoryModel::hasChildrenByIds($ids)) {
return $this->json(4002, '待删除数据存在子数据');
}
if (ArchivesCategoryModel::hasContentByIds($ids)) {
return $this->json(4002, '待删除数据存在内容文章');
}
ArchivesCategoryModel::deleteByIds($ids);
// Log::write(get_class().'Del', 'del', '涉及到的ID为'.implode(',', $ids));
return $this->json();
}
return $this->json(4001, '非法请求!');
}
/**
* 编辑
*
* @return Json|View
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
* @throws Exception
*/
public function edit()
{
$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
{
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()
{
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);
}
}

View File

@ -0,0 +1,173 @@
<?php
namespace app\controller\manager;
use app\model\Log;
use Exception;
use app\model\ArchivesModel as MArchivesModel;
use app\model\Archives;
use app\model\ArchivesModelField;
use think\exception\ValidateException;
use think\facade\Db;
use think\response\Json;
use think\response\View;
/**
* 文档模型管理
* Class ArchivesModel
* @package app\controller\manager
*/
class ArchivesModel extends Base
{
/**
* 删除
*
* @return Json
*/
public function del(): Json
{
if ($this->request->isPost()) {
$ids = input('post.ids/a', []);
if (empty($ids)) {
$ids[] = input('post.id/d');
}
$groupNames = MArchivesModel::whereIn('id', $ids)->column('name');
if (ArchivesModelField::whereIn('name', $groupNames)->count() > 0) {
return $this->json(4002, '模型下已存在字段,无法删除!');
}
MArchivesModel::deleteByIds($ids);
// Log::write(get_class().'Del', 'del', '涉及到的ID为'.implode(',', $ids));
return $this->json();
}
return $this->json(4001, '非法请求!');
}
/**
* 编辑
*
* @return Json|View
* @throws Exception
*/
public function edit()
{
$id = input('id/d', 0);
if (!$info = MArchivesModel::findById($id)) {
return $this->json(4001, '记录不存在');
}
if ($this->request->isPost()) {
$item = input('post.');
$validate = $this->validateByApi($item, [
'title|模型标题' => 'require',
'name|模型标识' => 'alphaDash|unique:archives_model,name,'.$id,
]);
if ($validate !== true) {
return $validate;
}
try {
ArchivesModelField::setFieldList($id, $info['name']);
$info->save($item);
return $this->json();
} catch (ValidateException $e) {
return $this->json(4001, $e->getError());
}
}
$this->data['item'] = $info;
return $this->view();
}
/**
* 单个字段编辑
*
* @return Json
* @throws Exception
*/
public function modify(): Json
{
if ($this->request->isPost()) {
$item = input('post.');
$validate = $this->validateByApi($item, [
'field' => 'require',
'value' => 'require',
]);
if ($validate !== true) {
return $validate;
}
if (!$info = MArchivesModel::findById($item['id'])) {
return $this->json(4001, '记录不存在');
}
$update = [$item['field'] => $item['value']];
try {
$info->save($update);
return $this->json();
} catch (ValidateException $e) {
return $this->json(4001, $e->getError());
}
}
return $this->json(4000, '非法请求');
}
/**
* 添加
*
* @return Json|View
* @throws Exception
*/
public function add()
{
if ($this->request->isPost()) {
$item = input('post.');
$validate = $this->validateByApi($item, [
'title|模型标题' => 'require',
'name|模型标识' => 'require|alphaDash|unique:archives_model',
]);
if ($validate !== true) {
return $validate;
}
try {
$model = MArchivesModel::create($item);
ArchivesModelField::setFieldList($model['id'], $model['name']);
return $this->json();
} catch (ValidateException $e) {
return $this->json(4001, $e->getError());
}
}
return $this->view();
}
/**
* 列表
*
* @return View|Json
* @throws Exception
*/
public function index()
{
if ($this->request->isPost()) {
$page = input('page/d', 1);
$limit = input('size/d', 20);
$items = MArchivesModel::findList([], [], $page, $limit, function ($q) {
return $q->order('sort', 'desc')->order('id', 'asc');
});
return $this->json(0, '操作成功', $items);
}
return $this->view();
}
}

View File

@ -0,0 +1,210 @@
<?php
namespace app\controller\manager;
use app\model\Log;
use Exception;
use app\model\ArchivesModelField as MArchivesModelField;
use app\model\ArchivesModel;
use think\exception\ValidateException;
use think\response\Json;
use think\response\View;
/**
* 文档模型字段管理
* Class ArchivesModelField
* @package app\controller\manager
*/
class ArchivesModelField extends Base
{
/**
* 删除
*
* @return Json
*/
public function del(): Json
{
if ($this->request->isPost()) {
$ids = input('post.ids/a', []);
if (empty($ids)) {
$ids[] = input('post.id/d');
}
MArchivesModelField::deleteByIds($ids);
// Log::write(get_class().'Del', 'del', '涉及到的ID为'.implode(',', $ids));
return $this->json();
}
return $this->json(4001, '非法请求!');
}
/**
* 编辑
*
* @return Json|View
* @throws Exception
*/
public function edit()
{
$id = input('id/d', 0);
if (!$info = MArchivesModelField::findById($id)) {
return $this->json(4001, '记录不存在');
}
if ($this->request->isPost()) {
$item = input('post.');
$validate = $this->validateByApi($item, [
'title|模型标题' => 'require',
'name|模型标识' => 'alphaDash',
]);
if ($validate !== true) {
return $validate;
}
try {
$info->save($item);
return $this->json();
} catch (ValidateException $e) {
return $this->json(4001, $e->getError());
}
}
$this->data['item'] = $info;
return $this->view();
}
/**
* 单个字段编辑
*
* @return Json
* @throws Exception
*/
public function modify(): Json
{
if ($this->request->isPost()) {
$item = input('post.');
$validate = $this->validateByApi($item, [
'field' => 'require',
'value' => 'require',
]);
if ($validate !== true) {
return $validate;
}
if (!$info = MArchivesModelField::findById($item['id'])) {
return $this->json(4001, '记录不存在');
}
$update = [$item['field'] => $item['value']];
try {
$info->save($update);
return $this->json();
} catch (ValidateException $e) {
return $this->json(4001, $e->getError());
}
}
return $this->json(4000, '非法请求');
}
/**
* 添加
*
* @return Json|View
* @throws Exception
*/
public function add()
{
$modelId = input('model_id/d', 0);
if ($this->request->isPost()) {
$item = input('post.');
$item['model_id'] = $modelId;
$validate = $this->validateByApi($item, [
'title|字段标题' => 'require',
'model_id|模型' => 'require|number|gt:0',
'name|字段标识' => 'require|alphaDash|unique:archives_model_field',
], ['model_id.gt' => '模型不存在']);
if ($validate !== true) {
return $validate;
}
try {
MArchivesModelField::create($item);
return $this->json();
} catch (ValidateException $e) {
return $this->json(4001, $e->getError());
}
}
$this->data['modelId'] = $modelId;
return $this->view();
}
/**
* 列表
*
* @return View|Json
* @throws Exception
*/
public function index()
{
$modelId = input('model_id/d', 0);
if (!$modelId) {
return $this->json(4001, '请选择正确的模型');
}
if ($this->request->isPost()) {
$page = input('page/d', 1);
$limit = input('size/d', 20);
$where[] = ['model_id', '=', $modelId];
$items = MArchivesModelField::findList($where, [], $page, $limit, function ($q) {
return $q->order('status', 'desc')->order('id', 'asc');
});
return $this->json(0, '操作成功', $items);
}
$this->data['modelId'] = $modelId;
return $this->view();
}
/**
* 同步字段
*
* @return Json
* @throws Exception
*/
public function sync(): Json
{
if ($this->request->isPost()) {
$modelId = input('model_id/d', 0);
if (!$modelId) {
return $this->json(4001, '模型错误');
}
if (!$info = ArchivesModel::findOne(['id' => $modelId])) {
return $this->json(4001, '模型不存在');
}
try {
MArchivesModelField::syncFieldList($modelId, $info['name']);
return $this->json();
} catch (ValidateException $e) {
return $this->json(4001, $e->getError());
}
}
return $this->json(4000, '非法请求');
}
}

View File

@ -0,0 +1,501 @@
<?php
namespace app\controller\manager;
use app\model\Log;
use app\service\AliOss;
use think\facade\Config;
use Exception;
use app\model\Attachment as AttachmentModel;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\exception\ValidateException;
use think\response\Json;
use think\response\View;
/**
* 附件管理 - 素材管理
* Class Attachment
* @package app\controller\manager
*/
class Attachment extends Base
{
protected $noNeedLogin = ['file', 'getSize', 'md5List', 'pathDirHandle', 'toOss', 'delLostFile', 'test'];
protected $DIRECTORY_SEPARATOR = "/";
protected function initialize()
{
parent::initialize(); // TODO: Change the autogenerated stub
$this->DIRECTORY_SEPARATOR = DIRECTORY_SEPARATOR == "\\" ? "/" : DIRECTORY_SEPARATOR;
}
/**
* 删除
*
* @return Json
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function del(): Json
{
if ($this->request->isPost()) {
$ids = input('post.ids/a', []);
if (empty($ids)) {
$ids[] = input('post.id/d');
}
$items = AttachmentModel::whereIn('id', $ids)->where('is_dir', AttachmentModel::COMMON_ON)->select();
if ($items->where('is_dir', AttachmentModel::COMMON_ON)->count()) {
$dirPaths = [];
foreach ($items->toArray() as $item) {
$dirPaths[] = $item['path'].$item['name'].$this->DIRECTORY_SEPARATOR;
}
if (AttachmentModel::where('path', 'in', $dirPaths)->count()) {
return $this->json(4001, '待删除目录下存在内容!');
}
}
AttachmentModel::deleteByIds($ids);
// Log::write(get_class().'Del', 'del', '涉及到的ID为'.implode(',', $ids));
return $this->json();
}
return $this->json(4001, '非法请求!');
}
/**
* 单个字段编辑
*
* @return Json
* @throws Exception
*/
public function modify(): Json
{
if ($this->request->isPost()) {
$item = input('post.');
$validate = $this->validateByApi($item, [
'field' => 'require',
'value' => 'require',
]);
if ($validate !== true) {
return $validate;
}
if (!$info = AttachmentModel::findById($item['id'])) {
return $this->json(4001, '记录不存在');
}
if ($item['field'] == 'name' && $info['is_dir'] == AttachmentModel::COMMON_ON) {
return $this->json(4002, '目录名称不能修改');
}
$update = [$item['field'] => $item['value']];
try {
$info->save($update);
return $this->json();
} catch (ValidateException $e) {
return $this->json(4001, $e->getError());
}
}
return $this->json(4000, '非法请求');
}
/**
* 添加
*
* @return Json|View
* @throws Exception
*/
public function add()
{
if ($this->request->isPost()) {
$item = input('post.');
$validate = $this->validateByApi($item, [
'title|合集标题' => 'require',
'user|虚拟用户' => 'require',
'headimg|虚拟用户头像' => 'require',
]);
if ($validate !== true) {
return $validate;
}
try {
$now = date('Y-m-d H:i:s');
$item['created_at'] = $now;
$item['created_by'] = $this->auth['user_id'];
AttachmentModel::create($item);
return $this->json();
} catch (ValidateException $e) {
return $this->json(4001, $e->getError());
}
}
return $this->view();
}
/**
* 添加文件夹
*
* @return Json|View
* @throws Exception
*/
public function addFolder()
{
if ($this->request->isPost()) {
$item = input('post.');
$validate = $this->validateByApi($item, [
'name|文件夹名称' => 'require|alphaDash|max:20',
'path|文件路径' => 'require',
]);
// 例 name=dir4 path=/storage/dir1/dir2/dir3
// 去首尾/
$path = trim($item['path'], $this->DIRECTORY_SEPARATOR);
// 全路径 如 /storage/dir1/dir2/dir3/dir4/ 注意前后都有/
$fullPath = $this->DIRECTORY_SEPARATOR.$path.$this->DIRECTORY_SEPARATOR.$item['name'].$this->DIRECTORY_SEPARATOR;
if ($validate !== true) {
return $validate;
}
AttachmentModel::pathDirHandle($fullPath);
return $this->json();
}
$path = input('path/s', AttachmentModel::ROOT_PATH);
$this->data['path'] = $path;
return $this->view();
}
/**
* 图片列表
*
* @return View|Json
* @throws Exception
*/
public function image()
{
$path = input('post.path', AttachmentModel::ROOT_PATH);
$path = $this->DIRECTORY_SEPARATOR.trim($path, $this->DIRECTORY_SEPARATOR).$this->DIRECTORY_SEPARATOR;
$path = str_replace("\\", "/", $path);
$type = input('type/s', 'image');
$selected = input('selected', false);
$multiple = input('multiple', false);
Config::load('extra/alioss', 'alioss');
Config::load('extra/base', 'base');
$config = config('alioss');
$baseConfig = config('base');
$oss = $baseConfig['oss'] == 'true' ? $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::load('extra/base', 'base');
$config = config('alioss');
$baseConfig = config('base');
$oss = $baseConfig['oss'] == 'true' ? $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');
$baseConfig = config('base');
$oss = $baseConfig['oss'] == 'true' ? $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::load('extra/base', 'base');
$config = config('alioss');
$baseConfig = config('base');
if ($baseConfig['oss'] != 'true') {
return $this->json('4000', '配置未开启OSS上传');
}
$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);
}
}

View File

@ -0,0 +1,105 @@
<?php
namespace app\controller\manager;
use app\controller\BaseController;
use app\service\File as FileTool;
use Exception;
use think\exception\ValidateException;
use think\response\Json;
use think\response\Redirect;
use think\response\View;
/**
* 控制器基础类
*/
class Base extends BaseController
{
protected $data = [];
protected $auth = null;
protected function initialize()
{
$this->middleware = [
'auth' => ['except' => array_merge($this->noNeedLogin, $this->noNeedRight)],
'log'
// 'jwt' => ['except' => $this->noNeedRight],
];
$this->auth = session('auth');
$this->data['member'] = $this->auth;
$this->data['_token'] = $this->auth['token'] ?? '';
$this->data['groupId'] = $this->auth['groupId'] ?? 0;
$this->fileDomain();
}
//变量赋值到模板
protected function view(string $template = '')
{
return view($template)->assign($this->data);
}
/**
* @param string $msg
* @param string|null $url
* @param string $data
* @param int $wait
* @return Redirect
*/
protected function error($msg = '', string $url = null, $data = '', int $wait = 3): Redirect
{
if (is_null($url)) {
$url = $this->request->isAjax() ? '' : 'javascript:history.back(-1);';
} elseif ($url) {
$url = (strpos($url, '://') || 0 === strpos($url, '/')) ? $url : $this->app->route->buildUrl($url);
}
$result = [
'code' => 0,
'msg' => $msg,
'data' => $data,
'url' => $url,
'wait' => $wait,
];
return $this->redirect(url('/manager/error/jump', $result));
}
public function __call($name, $args)
{
return $this->view('/manager/error/jump');
}
/**
* 验证器
*
* @param array $data
* @param $validate
* @param array $message
* @param bool $batch
* @return Redirect
* @throws Exception
*/
protected function validateError(array $data, $validate, array $message = [], bool $batch = false): Redirect
{
try {
parent::validate($data, $validate, $message, $batch);
} catch (ValidateException $e) {
$msg = $e->getMessage();
if ($batch) {
$msg = implode(',', $e->getError());
}
return $this->error($msg);
} catch (Exception $e) {
throw $e;
}
}
/**
* 文件域名前缀
*/
public function fileDomain()
{
$this->data['fileDomain'] = FileTool::getFileDomain();
}
}

View File

@ -0,0 +1,307 @@
<?php
namespace app\controller\manager;
use app\exception\RepositoryException;
use app\model\Account;
use app\model\AccountDataLog;
use app\model\AccountWithdrawalCommission;
use app\repository\AccountRepository;
use app\service\Math;
use Exception;
use think\facade\Db;
use think\response\Json;
use think\response\View;
/**
* 佣金
*
* Class Commission
* @package app\controller\manager
*/
class Commission extends Base
{
/**
* 操作用户佣金
* */
public function operation()
{
$accountId = input('account_id/d');
$type = input('type/s', 'commission');
if ($this->request->isPost()) {
$num = input("num/f", 1);
$remark = input("remark/s", "");
$account = AccountRepository::getInstance()->getModel()->findOne(["id" => $accountId], [], function ($q) {
return $q->lock(true);
});
if (empty($account)) {
return $this->json(4001, "用户不存在");
}
Db::startTrans();
try {
$saveData = [$type => ($account[$type] + $num)];
//写入用户记录
AccountDataLog::log($accountId,
"后台操作",
$num,
$type,
AccountDataLog::ACTION_ADMIN_OPERATION,
($account[$type] + $num),
$this->auth['nickname'] ?? "",
$this->auth['user_id'] ?? 0,
$remark
);
//保存用户
$account->save($saveData);
Db::commit();
return $this->json();
} catch (RepositoryException $e) {
Db::rollback();
return $this->json("4000", "佣金操作失败:".$e->getMessage());
} catch (Exception $e) {
Db::rollback();
\think\facade\Log::error('佣金操作失败'.$e->getMessage().' file:'.$e->getFile().' line:'.$e->getLine());
return $this->json("5003", "佣金操作失败");
}
}
$this->data["accountId"] = $accountId;
$this->data["type"] = $type;
return $this->view();
}
/**
* 处理搜索条件
*
* @param array $searchParams
* @return array[]
*/
protected function handleSearch(array $searchParams): array
{
$search = [];
$accountWhere = [];
if (!empty($searchParams)) {
foreach ($searchParams as $key => $param) {
if (!empty($param)) {
if (in_array($key, ['created_at', 'change_type', 'action'])) {
switch ($key) {
case 'created_at':
case 'action':
$search[] = ['self.'.$key, 'like', '%'.$param.'%'];
break;
case 'change_type':
$search[] = $param == 'in' ? ['num', '>', 0] : ['num', '<', 0];
break;
}
} else {
$accountWhere[] = [$key, 'like', '%'.$param.'%'];
}
}
}
}
return ['search' => $search, 'accountWhere' => $accountWhere];
}
/**
* 用户佣金排行 按降序排列
*
* @return Json|View
* @throws Exception
*/
public function index()
{
if ($this->request->isPost()) {
$page = input('page/d', 1);
$limit = input('size/d', 10);
$params = input('searchParams');
$keyword = $params['keyword'] ?? '';
$where[] = ['phone_active', '=', Account::COMMON_ON];
$items = AccountRepository::getInstance()->findList($where, [], $page, $limit, function ($q) use ($keyword) {
return $q->when(!empty($keyword), function ($qa) use ($keyword) {
$qa->where('real_name|mobile|nickname', 'like', '%'.$keyword.'%');
});
}, ["commission" => "desc", "id" => "desc"]);
return $this->json(0, '操作成功', $items);
}
return $this->view();
}
/**
* 查看单个用户的佣金记录
*
* @throws Exception
*/
public function personal()
{
$accountId = input("account_id/d", 0);
if ($this->request->isPost()) {
$page = input('page/d', 1);
$limit = input('size/d', 10);
$search = [];
$search[] = ["account_id", '=', $accountId];
$search[] = ["type", '=', AccountDataLog::TYPE_COMMISSION];
$items = AccountDataLog::findList($search, [], $page, $limit,
function ($q) {
return $q->with(["account"]);
}, ["id" => "desc"]);
$items['list'] = $items['list']->each(function ($item) {
$item->nickname = $item->account->nickname ?? '';
$item->real_name = $item->account->real_name ?? '';
$item->mobile = $item->account->mobile ?? '';
});
return $this->json(0, '操作成功', $items);
}
$this->data["accountId"] = $accountId;
return $this->view();
}
/**
* 查看所有用户的佣金记录
**/
public function log()
{
$accountId = input('account_id', 0);
if ($this->request->isPost()) {
$page = input('page/d', 1);
$limit = input('size/d', 10);
$searchParams = input('searchParams', []);
$accountWhere = [];
$search = [];
$search[] = ["type", '=', AccountDataLog::TYPE_COMMISSION];
if ($accountId > 0) {
$search[] = ["self.account_id", '=', $accountId];
}
$searchArr = $this->handleSearch($searchParams);
$search = !empty($searchArr['search']) ? array_merge($search, $searchArr['search']) : $search;
$accountWhere = !empty($searchArr['accountWhere']) ? array_merge($accountWhere, $searchArr['accountWhere']) : $accountWhere;
$field = ['self.*', 'a.nickname', 'a.real_name', 'a.mobile'];
$items = AccountDataLog::findList($search, $field, $page, $limit,
function ($q) use ($accountWhere) {
return $q->alias('self')
->join('account a', 'a.id = self.account_id')
->where($accountWhere);
}, ["id" => "desc"]);
return $this->json(0, '操作成功', $items);
}
$this->data['accountId'] = $accountId;
$this->data["changeAction"] = AccountDataLog::commissionAction();
return $this->view();
}
/**
* 提现列表
* */
public function withdrawalList()
{
if ($this->request->isPost()) {
$page = input('page/d', 1);
$limit = input('size/d', 10);
$items = AccountWithdrawalCommission::findList([], [], $page, $limit,
function ($q) {
return $q->with(["account"]);
}, ["id" => "desc"]);
return $this->json(0, '操作成功', $items);
}
return $this->view();
}
/**
* 审核提现
* */
public function check(): Json
{
$id = input("id/d");
$status = input("status/d", null);
if (empty($status)) {
return $this->json(4001, "错误的状态");
}
if (!$accountCoinWithdrawal = AccountWithdrawalCommission::findById($id, [])) {
return $this->json(4001, "该提现信息不存在");
}
if ($accountCoinWithdrawal['status'] != AccountWithdrawalCommission::$status_default) {
return $this->json(4001, "该提现不需要审核");
}
$account = AccountRepository::getInstance()->getModel()->findOne(["id" => $accountCoinWithdrawal['account_id']], [], function ($q) {
return $q->lock(true);
});
if (empty($account)) {
return $this->json(4001, "提现用户信息不存在");
}
Db::startTrans();
try {
//如果状态是审核失败 退回佣金 写入记录
if ($status == AccountWithdrawalCommission::$status_fail) {
//写入--积分佣金日志
AccountDataLog::log($accountCoinWithdrawal['account']['id'],
'提现佣金审核失败,退回',
$accountCoinWithdrawal['number'],
AccountDataLog::TYPE_COMMISSION,
AccountDataLog::ACTION_WITHDRAWAL_RETURN,
Math::add($accountCoinWithdrawal['account']['commission'], $accountCoinWithdrawal['number'])
);
//退回到账户
$account->save([
'commission' => Math::add($accountCoinWithdrawal['account']['commission'], $accountCoinWithdrawal['number'])
]);
}
//保存状态
$accountCoinWithdrawal->save([
"status" => $status,
"operator" => $this->auth["nickname"],
"audit_at" => date("Y-m-d H:i:s")
]);
Db::commit();
return $this->json();
} catch (RepositoryException $e) {
Db::rollback();
return $this->json(4000, $e->getMessage());
} catch (Exception $e) {
Db::rollback();
\think\facade\Log::error('佣金提现审核失败'.$e->getMessage().' file:'.$e->getFile().' line:'.$e->getLine());
return $this->json(5003, "审核失败");
}
}
/**
* 提现配置
* */
public function withdrawalConfig()
{
if ($this->request->isPost()) {
try {
$data = input("post.");
$php = var_export($data, true);
file_put_contents(config_path().'extra/commission_withdrawal'.'.php', '<?php'.PHP_EOL.'return '.$php.';');
return $this->json();
} catch (Exception $e) {
return $this->json(4001, $e->getMessage());
}
}
\think\facade\Config::load('extra/commission_withdrawal', 'commission_withdrawal');
$coinWithdrawal = config('commission_withdrawal');
$this->data["item"] = $coinWithdrawal;
return $this->view("/manager/config/commission_withdrawal");
}
}

View File

@ -0,0 +1,153 @@
<?php
namespace app\controller\manager;
use Exception;
use think\facade\Config as CConfig;
/**
* 额外配置
* Class Config
* @package app\controller\manager
*/
class Config extends Base
{
private string $extraPath = '';
protected function initialize()
{
parent::initialize();
$this->extraPath = config_path() . 'extra/';
if (!is_dir($this->extraPath)) {
if (is_writable(config_path())) {
mkdir($this->extraPath, 0777, true);
} else {
halt('请联系系统管理人员配置文件夹读写权限!请添加'.$this->extraPath.'文件夹的读写权限');
}
} elseif (!is_writable($this->extraPath)) {
halt('请联系系统管理人员配置文件夹读写权限!请添加'.$this->extraPath.'文件夹的读写权限');
}
}
public function other()
{
if ($this->request->isPost()) {
$data = input("post.");
unset($data['_token']);
$php = var_export($data, true);
file_put_contents($this->extraPath . 'other.php', '<?php' . PHP_EOL . 'return ' . $php . ';');
return $this->json();
} else {
CConfig::load('extra/other', 'other');
$this->data['item'] = config('other');
return $this->view();
}
}
public function wechat()
{
if ($this->request->isPost()) {
$data = input("post.");
unset($data['_token']);
$php = var_export($data, true);
file_put_contents($this->extraPath . 'wechat.php', '<?php' . PHP_EOL . 'return ' . $php . ';');
return $this->json();
} else {
CConfig::load('extra/wechat', 'wechat');
$this->data['item'] = config('wechat');
return $this->view();
}
}
public function alipay()
{
if ($this->request->isPost()) {
$data = input("post.");
unset($data['_token']);
$php = var_export($data, true);
file_put_contents($this->extraPath . 'alipay.php', '<?php' . PHP_EOL . 'return ' . $php . ';');
return $this->json();
} else {
CConfig::load('extra/alipay', 'alipay');
$this->data['item'] = config('alipay');
return $this->view();
}
}
/**
* 小程序配置
*/
public function miniProgram()
{
if ($this->request->isPost()) {
$data = input("post.");
unset($data['_token']);
unset($data['image']);
// 字符串与数组转换
$data['poster'] = empty($data['poster'] ?? '') ? [] : explode(',', $data['poster']);
$footBarList = array_values($data['footBar'] ?? []);
foreach ($footBarList as &$item) {
$icons = empty($item['icon']) ? [] : explode(',', $item['icon']);
$item['icon'] = array_filter($icons);
}
unset($item);
$data['footBar'] = $footBarList;
// 按sort排序
$recommendList = $data['recommend'] ?? [];
if (count($recommendList)) {
$recommendKeys = array_column($recommendList, 'sort');
array_multisort($recommendKeys, SORT_ASC, $recommendList);
}
$data['recommend'] = array_values($recommendList);
$php = var_export($data, true);
file_put_contents($this->extraPath . 'mini_program.php', '<?php' . PHP_EOL . 'return ' . $php . ';');
return $this->json();
} else {
CConfig::load('extra/mini_program', 'mini_program');
$data = config('mini_program');
if ($data) {
$data['poster'] = implode(',', $data['poster'] ?? []);
$footBarList = $data['footBar'] ?? [];
foreach ($footBarList as &$item) {
$item['icon'] = implode(',', $item['icon'] ?? []);
}
unset($item);
$data['footBar'] = $footBarList;
}
// 底部默认导航
$data['footBarIcons'] = [
['key' => 'home', 'name' => '首页', 'aux' => '图标大小为 40 * 40 第1图为默认图第2图为高亮图', 'multi' => 1],
['key' => 'category', 'name' => '分类', 'aux' => '图标大小为 40 * 40 第1图为默认图第2图为高亮图', 'multi' => 1],
['key' => 'my', 'name' => '我的', 'aux' => '图标大小为 40 * 40 第1图为默认图第2图为高亮图', 'multi' => 1],
['key' => 'cart', 'name' => '购物车', 'aux' => '图标大小为 120 * 120', 'multi' => 0],
];
$this->data = array_merge($this->data, $data);
return $this->view();
}
}
public function __call($name, $args)
{
if ($this->request->isPost()) {
try {
$data = input("post.");
$php = var_export($data, true);
file_put_contents(config_path().'extra/'.$name.'.php', '<?php'.PHP_EOL.'return '.$php.';');
return $this->json();
} catch (Exception $e) {
return $this->json(4001, $e->getMessage());
}
} else {
CConfig::load('extra/'.$name, $name);
$this->data['item'] = config($name);
$this->data['action'] = $name;
return $this->view('manager/config/'.$name);
}
}
}

View File

@ -0,0 +1,169 @@
<?php
namespace app\controller\manager;
use app\model\Log;
use Exception;
use app\model\ConfigGroup as ConfigGroupModel;
use app\model\Config as ConfigModel;
use think\exception\ValidateException;
use think\response\Json;
use think\response\View;
/**
* 配置分组
* Class ConfigGroup
* @package app\controller\manager
*/
class ConfigGroup extends Base
{
/**
* 删除
*
* @return Json
*/
public function del(): Json
{
if ($this->request->isPost()) {
$ids = input('post.ids/a', []);
if (empty($ids)) {
$ids[] = input('post.id/d');
}
$groupNames = ConfigGroupModel::whereIn('id', $ids)->column('name');
if (ConfigModel::whereIn('group', $groupNames)->count() > 0) {
return $this->json(4002, '分组下已创建配置,无法删除!');
}
ConfigGroupModel::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 = ConfigGroupModel::findById($id)) {
return $this->json(4001, '记录不存在');
}
if ($this->request->isPost()) {
$item = input('post.');
$validate = $this->validateByApi($item, [
'title|分组标题' => 'require',
'name|分组标识' => 'require|alphaDash|unique:config_group,name,'.$id,
]);
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 = ConfigGroupModel::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:config_group',
]);
if ($validate !== true) {
return $validate;
}
try {
ConfigGroupModel::create($item);
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 = ConfigGroupModel::findList([], [], $page, $limit, function ($q) {
return $q->order('sort', 'desc')->order('id', 'asc');
});
return $this->json(0, '操作成功', $items);
}
return $this->view();
}
}

View File

@ -0,0 +1,226 @@
<?php
namespace app\controller\manager;
use app\model\Log;
use app\repository\CmsRepository;
use Exception;
use app\model\Config as ConfigModel;
use app\model\ConfigGroup as ConfigGroupModel;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\exception\ValidateException;
use think\response\Json;
use think\response\View;
/**
* 配置项
* Class ConfigItem
* @package app\controller\manager
*/
class ConfigItem extends Base
{
/**
* 删除
*
* @return Json
*/
public function del(): Json
{
if ($this->request->isPost()) {
$ids = input('post.ids/a', []);
if (empty($ids)) {
$ids[] = input('post.id/d');
}
ConfigModel::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 = ConfigModel::findById($id)) {
return $this->json(4001, '记录不存在');
}
if ($this->request->isPost()) {
$item = input('post.');
$validate = $this->validateByApi($item, [
'title|配置标题' => 'require',
'group|配置分组' => 'require|alphaDash',
'value|配置值' => '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;
$this->data['jsonList'] = $this->categoryJson([$info['group']]);
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 = ConfigModel::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',
'group|配置分组' => 'require|alphaDash',
'name|配置变量名' => 'require|alphaDash|unique:config',
]);
if ($validate !== true) {
return $validate;
}
try {
ConfigModel::create($item);
return $this->json();
} catch (ValidateException $e) {
return $this->json(4001, $e->getError());
}
}
$this->data['jsonList'] = $this->categoryJson();
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)) {
if (is_string($param)) {
$where[] = [$key, 'like', '%'.$param.'%'];
} elseif (is_array($param)) {
$where[] = [$key, 'in', $param];
}
}
}
}
$items = ConfigModel::findList($where, [], $page, $limit, function ($q) {
return $q->order('sort', 'desc')->order('id', 'asc');
});
return $this->json(0, '操作成功', $items);
}
$this->data['categoryJson'] = json_encode($this->zTree(), JSON_UNESCAPED_UNICODE);
return $this->view();
}
/**
* @param array $selected
* @return false|string
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
private function categoryJson(array $selected = [])
{
$menus = $this->zTree($selected);
$categoryList = CmsRepository::getInstance()->handleSelectedList($menus);
return json_encode($categoryList, JSON_UNESCAPED_UNICODE);
}
/**
* @param array $selected
* @return array
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
private function zTree(array $selected = []): array
{
$menus = ConfigGroupModel::field('id,0 as pid,title,name,sort,true as open')
->order('sort', 'desc')
->order('id', 'asc')
->select()->toArray();
foreach ($menus as $k => $m) {
$menus[$k]['selected'] = in_array($m['name'], $selected);
$menus[$k]['disabled'] = in_array($m['name'], []);
}
return CmsRepository::getInstance()->buildMenuChild(0, $menus);
}
}

View File

@ -0,0 +1,11 @@
<?php
namespace app\controller\manager;
class Error
{
public function jump()
{
$param = request()->param();
return view()->assign($param);
}
}

View File

@ -0,0 +1,113 @@
<?php
namespace app\controller\manager;
use app\model\Log;
use app\repository\OrderRepository;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\exception\ValidateException;
use think\response\Json;
use think\response\View;
/**
* 快递公司
*
* Class Express
* @package app\controller\manager
*/
class Express extends Base
{
/**
* 删除
*
* @return Json
*/
public function del()
{
if ($this->request->isPost()) {
$ids = input('post.ids/a', []);
if (empty($ids)) {
$ids[] = input('post.id/d');
}
$where[] = ['id', 'in', $ids];
OrderRepository::getInstance()->delExpress($where);
Log::write('express del', 'del', '删除了快递涉及到的ID为'.implode(',', $ids));
return $this->json();
}
return $this->json(4001, '非法请求!');
}
/**
* 添加
*
* @return Json|View
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function edit()
{
$id = input('id/d', 0);
if (!$info = OrderRepository::getInstance()->expressInfo($id)) {
return $this->json(4001, '数据不存在');
}
if ($this->request->isPost()) {
$item = input('post.item/a');
try {
unset($item['id']);
OrderRepository::getInstance()->editExpress($item, ['id' => $id]);
return $this->json();
} catch (ValidateException $e) {
return $this->json(4001, $e->getError());
}
}
$this->data['item'] = $info;
$this->data['id'] = $id;
return $this->view();
}
/**
* 添加
*
* @return Json|View
*/
public function add()
{
if ($this->request->isPost()) {
$item = input('post.item/a');
try {
OrderRepository::getInstance()->addExpress($item);
return $this->json();
} catch (ValidateException $e) {
return $this->json(4001, $e->getError());
}
}
return $this->view();
}
/**
* 快递列表
*
* @return View
* @throws DbException
*/
public function index(): View
{
$size = input('size/d', 20);
$urlQuery = input('get.');
$where = [];
$where['size'] = $size;
$this->data['items'] = OrderRepository::getInstance()->express($where, $urlQuery);
return $this->view();
}
}

View File

@ -0,0 +1,77 @@
<?php
namespace app\controller\manager;
use app\model\Feedback as FeedbackModel;
use app\model\FeedbackType;
use app\model\Log;
use Exception;
use think\response\Json;
use think\response\View;
/**
* 意见反馈
*
* Class Feedback
* @package app\controller\manager
*/
class Feedback extends Base
{
/**
* 删除
*
* @return Json
*/
public function del(): Json
{
if ($this->request->isPost()) {
$ids = input('post.ids/a', []);
if (empty($ids)) {
$ids[] = input('post.id/d');
}
FeedbackModel::deleteByIds($ids);
Log::write(get_class().'Del', 'del', '涉及到的ID为'.implode(',', $ids));
return $this->json();
}
return $this->json(4001, '非法请求!');
}
/**
* 列表
*
* @return View|Json
* @throws Exception
*/
public function index()
{
if ($this->request->isPost()) {
$page = input('page/d', 1);
$limit = input('size/d', 20);
$searchParams = input('searchParams');
$search = [];
if ($searchParams) {
foreach ($searchParams as $key => $param) {
if ($param) {
$search[] = [$key, 'like', '%'.$param.'%'];
}
}
}
$items = FeedbackModel::findList($search, [], $page, $limit, function ($q) {
return $q->with(['type', 'account'])->order('created_at', 'desc');
});
$items['list'] = $items['list']->each(function ($item) {
$item->feedback_type = $item->type->title ?? '';
$item->nickname = $item->account->nickname ?? '';
$item->mobile = $item->account->mobile ?? '';
});
return $this->json(0, '操作成功', $items);
}
$this->data['typeList'] = FeedbackType::select();
return $this->view();
}
}

View File

@ -0,0 +1,143 @@
<?php
namespace app\controller\manager;
use app\model\{File as MFile, Archives, Block, Category, Link, Slide, Log};
use app\service\Tool;
class File extends Base
{
protected $noNeedLogin = ['delPath', 'del', 'unuse', 'getAllFilesInUse', 'list', 'index'];
//删除磁盘上的文件
public function delPath()
{
if ($this->request->isPost()) {
$paths = input('post.paths/a');
if(!empty($paths)){
foreach($paths as $path){
Tool::delFile($path);
}
Log::write('file', 'delPath', '批量删除了磁盘文件,涉及到的文件路径为:' . implode(',', $paths));
return $this->json();
}
return $this->json(2, '待删除文件列表为空');
}
return $this->json(1, '非法请求!');
}
//删除文件记录
public function del()
{
if ($this->request->isPost()) {
$ids = input('post.ids/a');
if(empty($ids) || !is_array($ids)) {
return $this->json(2, '参数错误,请核对之后再操作!');
}
$items = MFile::getListByIds($ids);
if(!empty($items)){
$delIds = [];
foreach($items as $item){
$delIds[] = $item['id'];
if($item['type'] == MFile::IMG){
Tool::delFile($item['src'], 1);
}else{
Tool::delFile($item['src']);
}
}
MFile::destroy($delIds);
Log::write('file', 'del', '批量删除了文件涉及到的文件ID为' . implode(',', $delIds));
return $this->json();
}else{
return $this->json(3, '待删除文件列表为空');
}
}
return $this->json(1, '非法请求!');
}
/**
* 未使用文件列表,
* 1. 遍历数据库中使用的图片视频及文件路径
* 2. 遍历上传目录中的文件
* 3. 数据对比,找出存在目录中的文件&不在数据库中的文件
* 4. 页面上显示查找出来的文件
*/
public function unuse()
{
$filesInUse = $this->getAllFilesInUse(); //数据库中在使用的文件
$rootPath = app()->getRootPath();
$uploadPath = $rootPath . 'storage';
$uploadedFiles = getAllFilesByPath($uploadPath, $rootPath); //磁盘上上传的文件
$files = MFile::getAll();
$dbUploadedFiles = []; //数据库中上传的文件
foreach($files as $file){
$src = trim($file['src']);
if(!empty($src)){
$key = getKeyByPath($src);
$dbUploadedFiles[$key] = $file;
}
}
$uploadedNotInUseFiles = array_diff_key($uploadedFiles, $filesInUse); //磁盘上上传未使用的文件
$dbUploadedNotInUseFiles = array_diff_key($dbUploadedFiles, $filesInUse); //数据库中上传未使用的文件
$bothNotInUseFiles = array_intersect_key($uploadedNotInUseFiles, $dbUploadedNotInUseFiles); //磁盘和数据库中,两边都未使用
$this->data['uploadedNotInUseFiles'] = $uploadedNotInUseFiles;
$this->data['dbUploadedNotInUseFiles'] = $dbUploadedNotInUseFiles;
$this->data['bothNotInUseFilesKey'] = array_keys($bothNotInUseFiles);
return $this->view();
}
//获取所有在使用的文件
private function getAllFilesInUse()
{
$files = [];
$blockFiles = Block::getFilesInUse();
if(!empty($blockFiles)){
$files = array_merge($files, $blockFiles);
}
$slideFiles = Slide::getFilesInUse();
if(!empty($slideFiles)){
$files = array_merge($files, $slideFiles);
}
$linkFiles = Link::getFilesInUse();
if(!empty($linkFiles)){
$files = array_merge($files, $linkFiles);
}
$categoryFiles = Category::getFilesInUse();
if(!empty($categoryFiles)){
$files = array_merge($files, $categoryFiles);
}
$articleFiles = Archives::getFilesInUse();
if(!empty($articleFiles)){
$files = array_merge($files, $articleFiles);
}
return $files;
}
//ajax获取文件列表
public function list()
{
if($this->request->isAjax()){
$page = input('param.page/d', 1);
$size = input('param.size/d', 20);
if(!is_integer($page) || $page < 1){
$page = 1;
}
if (!is_integer($size) || $size < 1) {
$size = 20;
}
$type = input('param.type', '');
if(!in_array($type, array_keys(MFile::getTypes()))){
$type = '';
}
$items = MFile::getList($type, $page, $size);
return $this->json(0, 'ok', $items);
}
return $this->json(1, '无此操作');
}
//列表
public function index()
{
$items = MFile::getListPage();
$this->data['items'] = $items;
$this->data['types'] = MFile::getTypes();
return $this->view();
}
}

View File

@ -0,0 +1,101 @@
<?php
namespace app\controller\manager;
use app\model\Overview;
use app\model\Spu;
use app\repository\CmsRepository;
use Exception;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\response\Json;
use think\response\View;
use app\model\Member;
use app\model\Menu;
class Index extends Base
{
protected $noNeedLogin = ['index', 'init'];
/**
* 后台初始页面 随后进入dashboard页面
*
* @return View
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function index(): View
{
$auth = session('auth');
$this->data['user'] = Member::findById($auth['user_id'] ?? 0, ['id', 'username', 'nickname', 'mobile']);
return $this->view();
}
/**
* 控制台
*
* @return Json|View
* @throws Exception
*/
public function dashboard()
{
if ($this->request->isPost()) {
$page = input('page/d', 1);
$limit = input('size/d', 10);
$items = Overview::findList([], [], $page, $limit, function ($q) {
return $q->with(['staff', 'account'])->order('customer', 'desc')
->order('shares', 'desc')->order('views', 'desc')->order('asks', 'desc');
});
$items['list'] = $items['list']->each(function ($item) {
$item->nickname = $item->account->nickname ?? '';
$item->real_name = $item->account->real_name ?? '';
$item->staff_name = $item->staff->name ?? '';
$item->phone = $item->account->mobile ?? '';
});
return $this->json(0, '操作成功', $items);
}
$count['spu'] = Spu::count();
$this->data['count'] = $count;
return $this->view();
}
/**
* 菜单初始化
*
* @return Json
*/
public function init(): Json
{
$res = [];
$res['homeInfo'] = ['title' => '控制台', 'href' => "manager/index/dashboard"];
$res['logoInfo'] = ['title' => '佩丽商城', 'href' => "", 'image' => '/static/manager/image/logo.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);
}
}

View File

@ -0,0 +1,56 @@
<?php
namespace app\controller\manager;
use app\model\Log as LogModel;
use Exception;
use think\response\Json;
use think\response\View;
/**
* 日志
*
* Class Feedback
* @package app\controller\manager
*/
class Log extends Base
{
/**
* 列表
*
* @return View|Json
* @throws Exception
*/
public function index()
{
if ($this->request->isPost()) {
$page = input('page/d', 1);
$limit = input('size/d', 20);
$searchParams = input('searchParams');
$search = [];
if ($searchParams) {
foreach ($searchParams as $key => $param) {
if ($param) {
if ($key == 'begin_time') {
$begin = strtotime($param.' 00:00:00');
$search[] = ['create_time', '>', $begin];
} elseif ($key == 'end_time') {
$end = strtotime($param.' 23:59:59');
$search[] = ['create_time', '<', $end];
} else {
$search[] = [$key, 'like', '%'.$param.'%'];
}
}
}
}
$items = LogModel::findList($search, [], $page, $limit, function ($q) {
return $q->with(['memberName'])->order('create_time', 'desc');
});
return $this->json(0, '操作成功', $items);
}
return $this->view();
}
}

View File

@ -0,0 +1,76 @@
<?php
namespace app\controller\manager;
use app\service\Jwt;
use Exception;
use app\model\{Member, AuthRule, LoginLog};
use app\controller\BaseController;
use think\response\Json;
use think\response\View;
class Login extends BaseController
{
protected $noNeedLogin = ['index'];
/**
* @return View|Json
* @throws Exception
*/
public function index()
{
if (request()->isPost()) {
$param = input('post.data');
$username = trim($param['username']);
$password = trim($param['password']);
$captcha = trim($param['captcha'] ?? '');
if (!captcha_check($captcha)) {
return $this->json(4001, '验证码错误'.$captcha);
}
if (empty($username) || empty($password)) {
return $this->json(4001, '用户名和密码不能为空');
}
$member = Member::getByUserName($username);
if (empty($member)) {
return $this->json(4002, '用户名或密码错误');
}
if ($member['password'] != md5($password.$username)) {
return $this->json(4003, '用户名或密码错误');
}
if ($member['status'] != Member::STATUS_NORMAL) {
return $this->json(4004, '账号已被禁用');
}
$userInfo = [
'user_id' => $member['id'],
'username' => $member['username'],
'nickname' => $member['nickname'],
'account_id' => $member['account_id'],//绑定的前台用户ID
];
$jwtToken = Jwt::generate($userInfo, env('app.expire', 7200));
$userInfo['token'] = $jwtToken;//jwt生成token
//记录最后登陆时间
$ip = request()->ip();
$time = time();
Member::updateById($member['id'], [
'login_time' => $time,
'login_ip' => $ip
]);
LoginLog::create([
'member_id' => $member['id'],
'name' => $member['username'],
'ip' => $ip,
'create_time' => $time
]);
session('auth', $userInfo);
return $this->json(0, 'success', ['url' => '/manager']);
}
$viewData = [];
return view()->assign($viewData);
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace app\controller\manager;
use app\controller\BaseController;
use think\response\Redirect;
class Logout extends BaseController
{
protected $noNeedLogin = ['index'];
/**
* 退出
*
* @return Redirect
*/
public function index(): Redirect
{
session(null);
return redirect(url('/manager/login/index'));
}
}

View File

@ -0,0 +1,387 @@
<?php
namespace app\controller\manager;
use app\model\Log;
use app\model\Member as MemberModel;
use app\model\Role as RoleModel;
use Exception;
use tauthz\facade\Enforcer;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\exception\ValidateException;
use think\facade\Db;
use think\response\Json;
use think\response\Redirect;
use think\response\View;
/**
* (后台)人员管理
*
* Class Member
* @package app\controller\manager
*/
class Member extends Base
{
/**
* 删除
*
* @return Json
*/
public function del(): Json
{
if ($this->request->isPost()) {
$ids = input('post.ids/a', []);
if (empty($ids)) {
$ids[] = input('post.id/d');
}
MemberModel::deleteByIds($ids);
foreach ($ids as $id) {
Enforcer::deleteRolesForUser($id);
}
Log::write(get_class().'Del', 'del', '涉及到的ID为'.implode(',', $ids));
return $this->json();
}
return $this->json(4001, '非法请求!');
}
/**
* 个人详情
*
* @return Json|View|Redirect
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
* @throws Exception
*/
public function profile()
{
$id = $this->auth['user_id'] ?? 0;
if (!$item = MemberModel::findById($id)) {
if ($this->request->isAjax()) {
return $this->json(4001, '记录不存在');
}
return $this->error('记录不存在');
}
if ($this->request->isPost()) {
$post = input('post.');
$validate = $this->validateByApi($post, [
'mobile|手机号' => 'require|unique:member,mobile,'.$id,
'nickname|昵称' => 'require|chsAlphaNum|min:2|max:10',
'remark|备注信息' => 'max:255',
]);
if ($validate !== true) {
return $validate;
}
if (!checkMobile($post['mobile'])) {
return $this->json(4002, '请输入正确的手机号码');
}
try {
$item->save($post);
return $this->json();
} catch (ValidateException $e) {
return $this->json(4001, $e->getError());
}
}
$this->data['item'] = $item;
return $this->view();
}
/**
* 编辑
*
* @return Json|View
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
* @throws Exception
*/
public function edit()
{
$id = input('id/d', 0);
if (!$info = MemberModel::findById($id)) {
return $this->json(4001, '记录不存在');
}
if ($this->request->isPost()) {
$item = input('post.');
$validate = $this->validateByApi($item, [
'mobile|手机号' => 'require|unique:member,mobile,'.$id,
'nickname|昵称' => 'require|chsAlphaNum|min:2|max:10',
'remark|备注信息' => 'max:255',
]);
if ($validate !== true) {
return $validate;
}
if (!checkMobile($item['mobile'])) {
return $this->json(4002, '请输入正确的手机号码');
}
$roles = [];
if ($item['roles']) {
$roles = $item['roles'];
$item['roles'] = implode(',', $item['roles']);
}
Db::startTrans();
try {
$info->save($item);
//删除所有角色
Enforcer::deleteRolesForUser($id);
//新增角色
foreach ($roles as $role) {
Enforcer::addRoleForUser($id, $role);
}
Db::commit();
return $this->json();
} catch (ValidateException $e) {
Db::rollback();
return $this->json(4001, $e->getError());
}
}
$this->data['item'] = $info;
$this->data['roleJson'] = $this->roleJson(explode(',', $info['roles']));
return $this->view();
}
/**
* 单个字段编辑
*
* @return Json
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
* @throws Exception
*/
public function modify(): Json
{
if ($this->request->isPost()) {
$item = input('post.');
$validate = $this->validateByApi($item, [
'field' => 'require',
'value' => 'require',
]);
if ($validate !== true) {
return $validate;
}
if (!$info = MemberModel::findById($item['id'])) {
return $this->json(4001, '记录不存在');
}
$update = [$item['field'] => $item['value']];
try {
$info->save($update);
return $this->json();
} catch (ValidateException $e) {
return $this->json(4001, $e->getError());
}
}
return $this->json(4000, '非法请求');
}
/**
* 添加
*
* @return Json|View
* @throws Exception
*/
public function add()
{
if ($this->request->isPost()) {
$item = input('post.');
$validate = $this->validateByApi($item, [
'username|用户名' => 'require|alphaDash|min:4|max:16|unique:member',
'mobile|手机号' => 'require|unique:member',
'nickname|昵称' => 'require|chsAlphaNum|min:2|max:10',
'password|密码' => 'require|min:4|max:16',
'remark|备注信息' => 'max:255',
]);
if ($validate !== true) {
return $validate;
}
if (!checkMobile($item['mobile'])) {
return $this->json(4002, '请输入正确的手机号码');
}
$roles = [];
if ($item['roles']) {
$roles = $item['roles'];
$item['roles'] = implode(',', $item['roles']);
}
Db::startTrans();
try {
$item['password'] = md5($item['password'].$item['username']);
$member = MemberModel::create($item);
foreach ($roles as $role) {
Enforcer::addRoleForUser($member['id'], $role);
}
Db::commit();
return $this->json();
} catch (ValidateException $e) {
Db::rollback();
return $this->json(4001, $e->getError());
}
}
$this->data['roleJson'] = $this->roleJson();
return $this->view();
}
/**
* 修改密码
*
* @return Json|View|Redirect
* @throws Exception
*/
public function password()
{
$id = input('id/d', 0);
if (!$item = MemberModel::findById($id)) {
if ($this->request->isAjax()) {
return $this->json(4001, '记录不存在');
}
return $this->error('记录不存在');
}
if ($this->request->isPost()) {
$post = input('post.');
$validate = $this->validateByApi($post, [
'password|密码' => 'require|confirm',
]);
if ($validate !== true) {
return $validate;
}
$password = md5($post['password'].$item['username']);
try {
$item->save(['password' => $password]);
return $this->json();
} catch (ValidateException $e) {
return $this->json(4001, $e->getError());
}
}
$this->data['item'] = $item;
return $this->view();
}
/**
* 个人修改密码
*
* @return Json|View
* @throws Exception
*/
public function myPassword()
{
$id = $this->auth['user_id'] ?? 0;
if (!$item = MemberModel::findById($id)) {
return $this->json(4001, '记录不存在');
}
if ($this->request->isPost()) {
$post = input('post.');
$validate = $this->validateByApi($post, [
'old-password|旧密码' => 'require',
'password|密码' => 'require|confirm',
]);
if ($validate !== true) {
return $validate;
}
if ($item['password'] !== md5($post['old-password'].$item['username'])) {
return $this->json(4002, '原始密码错误');
}
$password = md5($post['password'].$item['username']);
try {
$item->save(['password' => $password]);
return $this->json();
} catch (ValidateException $e) {
return $this->json(4001, $e->getError());
}
}
$this->data['item'] = $item;
return $this->view();
}
/**
* 列表
*
* @return View|Json
* @throws Exception
*/
public function index()
{
if ($this->request->isPost()) {
$page = input('page/d', 1);
$limit = input('size/d', 20);
$searchParams = input('searchParams');
$where = [];
if ($searchParams) {
foreach ($searchParams as $key => $param) {
if (!empty($param)) {
$where[] = [$key, 'like', '%'.$param.'%'];
}
}
}
$items = MemberModel::findList($where, [], $page, $limit, function ($q) {
return $q->order('id', 'desc');
});
return $this->json(0, '操作成功', $items);
}
return $this->view();
}
/**
* 构造角色json数据
*
* @param array $selected
* @return false|string
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
private function roleJson(array $selected = [])
{
$roles = RoleModel::where('status', RoleModel::STATUS_NORMAL)
->order('sort', 'desc')
->select()
->toArray();
foreach ($roles as $k => $m) {
$roles[$k]['checked'] = in_array($m['id'], $selected);
$roles[$k]['spread'] = true;
}
return json_encode($roles, JSON_UNESCAPED_UNICODE);
}
}

View File

@ -0,0 +1,263 @@
<?php
namespace app\controller\manager;
use app\repository\CmsRepository;
use app\model\Log;
use app\model\Menu as MenuModel;
use app\validate\MenuValidate;
use Exception;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\exception\ValidateException;
use think\facade\Db;
use think\response\Json;
use think\response\View;
/**
* 菜单管理
*
* Class Menu
* @package app\controller\manager
*/
class Menu extends Base
{
/**
* 删除
*
* @return Json
*/
public function del(): Json
{
if ($this->request->isPost()) {
$ids = input('post.ids/a', []);
if (empty($ids)) {
$ids[] = input('post.id/d');
}
$repo = CmsRepository::getInstance();
if ($repo->hasChildrenMenuByIds($ids)) {
return $this->json(4002, '待删除数据存在子数据');
}
$repo->delMenuByIds($ids);
Log::write('menuDel', 'del', '删除了菜单涉及到的ID为'.implode(',', $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);
}
}

View File

@ -0,0 +1,161 @@
<?php
namespace app\controller\manager;
use app\exception\TraitException;
use app\model\Log;
use app\model\Message AS MessageModel;
use app\model\ScriptManagement AS ScriptManagementModel;
use app\repository\AccountRepository;
use think\facade\Db;
class Message extends Base
{
public function index()
{
$repo = AccountRepository::getInstance();
if ($this->request->isPost()) {
$page = $this->request->param('page/d', 1);
$size = $this->request->param('size/d', 30);
$whereMap = [];
$orders = ['send_at' => 'desc', 'id' => 'desc'];
$whereMap[] = ['type', '<>', MessageModel::TYPE_REMINDERS];
$whereMap[] = ['is_push', '=', MessageModel::COMMON_OFF];
$res = $repo->messageList($whereMap, [], $page, $size, null, $orders);
$list = $res['list'];
$msgTypeTextList = $repo->messageTypeTextList();
$msgTargetTextList = $repo->messageTargetTextList();
foreach ($list as $item) {
$item['type_text'] = $msgTypeTextList[$item['type']] ?? '';
$item['target_text'] = $msgTargetTextList[$item['target']] ?? '';
}
$res['list'] = $list;
return $this->json(0, 'success', $res);
}
return $this->view();
}
public function add()
{
$repo = AccountRepository::getInstance();
if ($this->request->isPost()) {
$item = $this->request->param('item/a', []);
$targetListStr = $this->request->param('target_list_str/s', '');
$fdata = $this->request->param('fdata/a', []);
if (empty($fdata)) {
return $this->json(4001, "发送时间必填");
}
foreach ($fdata as $fdatum) {
if (!isset($fdatum['send_at'])||empty($fdatum['send_at'])) {
return $this->json(4001, "发送时间必填");
}
}
unset($item["send_at"]);
Db::startTrans();
try {
$type = $item['type'] ?? '';
$target = $item['target'] ?? '';
$targetList = empty($targetListStr) ? [] : explode(',', $targetListStr);
foreach ($fdata as $sitem){
$item["send_at"] = $sitem['send_at'];
$item["content"] = $sitem['content'];
$repo->addMessage($type, $target, $targetList, $item);
}
Db::commit();
return $this->json();
} catch (TraitException $e) {
Db::rollback();
return $this->json(4001, $e->getMessage());
}
}
$this->data['typeList'] = $repo->messageTypeTextList();
$this->data['targetList'] = $repo->messageTargetTextList();
$scriptManagement = ScriptManagementModel::getAll();
$this->data["script_management"] = $scriptManagement;
$this->data["script_management_json"] = json_encode($scriptManagement);
return $this->view();
}
public function edit()
{
$repo = AccountRepository::getInstance();
$id = $this->request->param('id/d', 0);
$msg = $repo->messageInfo($id);
if (empty($msg)) {
return $this->json(4000, '没有相关的消息记录');
}
if ($this->request->isPost()) {
$item = $this->request->param('item/a', []);
$targetListStr = $this->request->param('target_list_str/s', '');
try {
$type = $item['type'] ?? '';
$target = $item['target'] ?? '';
$targetList = empty($targetListStr) ? [] : explode(',', $targetListStr);
$repo->editMessage($id, $type, $target, $targetList, $item);
} catch (TraitException $e) {
return $this->json(4001, $e->getMessage());
}
return $this->json();
}
$targetAids = empty($msg['target_list'] ?? '') ? [] : explode(',', $msg['target_list']);
$whereMap[] = ['id', 'in', $targetAids];
$targetList = $repo->findList($whereMap)['list']->toArray();
foreach ($targetList as &$item) {
$item['account_desc2'] = $item['nickname'].'【姓名:'.$item['real_name'].'】';
$item['selected'] = true;
}
$this->data['id'] = $id;
$this->data['item'] = $msg;
$this->data['targetListJson'] = json_encode($targetList, JSON_UNESCAPED_UNICODE);
$this->data['typeList'] = $repo->messageTypeTextList();
$this->data['targetList'] = $repo->messageTargetTextList();
$scriptManagement = ScriptManagementModel::getAll();
$this->data["script_management"] = $scriptManagement;
$this->data["script_management_json"] = json_encode($scriptManagement);
return $this->view();
}
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)) {
AccountRepository::getInstance()->deleteMessages($ids);
Log::write(get_class(), 'del', '删除了message涉及到的ID为'.implode(',', $ids));
}
return $this->json();
}
}

View File

@ -0,0 +1,58 @@
<?php
namespace app\controller\manager;
use app\model\AccountRecord;
use app\model\Overview as OverviewModel;
use app\model\AccountRole;
use app\model\Appointment;
use app\model\Account;
use Exception;
use think\response\Json;
use think\response\View;
class Overview extends Base
{
/**
* 客户分析
*
* @return Json|View
* @throws Exception
*/
public function customer()
{
if ($this->request->isPost()) {
$page = input('page/d', 1);
$limit = input('size/d', 10);
$params = input('searchParams');
$keyword = $params['keyword'] ?? '';
$items = OverviewModel::findList([], [], $page, $limit, function ($q) use ($keyword) {
return $q->alias('o')->leftJoin('account a', 'a.id = o.account_id')
->field('o.*,a.nickname,a.real_name,a.mobile')
->when(!empty($keyword), function ($q) use ($keyword) {
$q->where('a.nickname|a.real_name|a.mobile', 'like', '%'.$keyword.'%');
})
->where('a.phone_active', Account::COMMON_ON)
->order('customer', 'desc')
->order('shares', 'desc')
->order('views', 'desc')
->order('asks', 'desc');
});
$items['list'] = $items['list']->each(function ($item) {
$item->position = '客户';
});
return $this->json(0, '操作成功', $items);
}
$today = date('Y-m-d 00:00:00');
$count['new'] = Account::where('created_at', '>', $today)->count();
$count['total'] = Account::where('phone_active', Account::COMMON_ON)->count();
$this->data['count'] = $count;
return $this->view();
}
}

View File

@ -0,0 +1,150 @@
<?php
namespace app\controller\manager;
use app\exception\RepositoryException;
use app\exception\TraitException;
use app\model\Log;
use app\model\Message as MessageModel;
use app\model\ScriptManagement as ScriptManagementModel;
use app\repository\AccountRepository;
use app\service\ali\Sms;
use app\service\wx\WechatApplets;
use GuzzleHttp\Exception\GuzzleException;
use think\facade\Db;
class Push extends Base
{
public function index()
{
$repo = AccountRepository::getInstance();
if ($this->request->isPost()) {
$page = $this->request->param('page/d', 1);
$size = $this->request->param('size/d', 30);
$whereMap = [];
$orders = ['send_at' => 'desc', 'id' => 'desc'];
$whereMap[] = ['type', '<>', MessageModel::TYPE_REMINDERS];
$whereMap[] = ['is_push', '=', MessageModel::COMMON_ON];
$res = $repo->messageList($whereMap, [], $page, $size, null, $orders);
$list = $res['list'];
$msgTypeTextList = $repo->messageTypeTextList();
$msgTargetTextList = $repo->messageTargetTextList();
foreach ($list as $item) {
$item['type_text'] = $msgTypeTextList[$item['type']] ?? '';
$item['target_text'] = $msgTargetTextList[$item['target']] ?? '';
}
$res['list'] = $list;
return $this->json(0, 'success', $res);
}
return $this->view();
}
public function add()
{
$repo = AccountRepository::getInstance();
if ($this->request->isPost()) {
$item = $this->request->param('item/a', []);
$targetListStr = $this->request->param('target_list_str/s', '');
Db::startTrans();
try {
$targetList = empty($targetListStr) ? [] : explode(',', $targetListStr);
$repo->createMessage($item, $targetList);
Db::commit();
return $this->json();
} catch (RepositoryException | TraitException $e) {
Db::rollback();
return $this->json(4001, $e->getMessage());
} catch (\Exception $e) {
Db::rollback();
\think\facade\Log::error('[添加推送失败]'.$e->getMessage());
return $this->json(5001, '添加推送失败');
}
}
$this->data['subscribeTempList'] = WechatApplets::msgTemplateList();
$this->data['subscribeTempParams'] = json_encode(WechatApplets::msgTemplateParams(), JSON_UNESCAPED_UNICODE);
$this->data['smsTempList'] = Sms::templateList();
$this->data['smsTempParams'] = json_encode(Sms::templateParams(), JSON_UNESCAPED_UNICODE);
$this->data['targetList'] = $repo->messageTargetTextList();
return $this->view();
}
public function edit()
{
$repo = AccountRepository::getInstance();
$id = $this->request->param('id/d', 0);
$msg = $repo->messageInfo($id);
if (empty($msg)) {
return $this->json(4000, '没有相关的消息记录');
}
if ($this->request->isPost()) {
$item = $this->request->param('item/a', []);
$targetListStr = $this->request->param('target_list_str/s', '');
try {
$type = $item['type'] ?? '';
$target = $item['target'] ?? '';
$targetList = empty($targetListStr) ? [] : explode(',', $targetListStr);
$repo->editMessage($id, $type, $target, $targetList, $item);
} catch (TraitException $e) {
return $this->json(4001, $e->getMessage());
}
return $this->json();
}
$targetAids = empty($msg['target_list'] ?? '') ? [] : explode(',', $msg['target_list']);
$whereMap[] = ['id', 'in', $targetAids];
$targetList = $repo->findList($whereMap)['list']->toArray();
foreach ($targetList as &$item) {
$item['account_desc2'] = $item['nickname'].'【姓名:'.$item['real_name'].'】';
$item['selected'] = true;
}
$this->data['id'] = $id;
$this->data['item'] = $msg;
$this->data['targetListJson'] = json_encode($targetList, JSON_UNESCAPED_UNICODE);
$this->data['typeList'] = $repo->messageTypeTextList();
$this->data['targetList'] = $repo->messageTargetTextList();
$scriptManagement = ScriptManagementModel::getAll();
$this->data["script_management"] = $scriptManagement;
$this->data["script_management_json"] = json_encode($scriptManagement);
return $this->view();
}
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)) {
AccountRepository::getInstance()->deleteMessages($ids);
Log::write(get_class(), 'del', '删除了message涉及到的ID为'.implode(',', $ids));
}
return $this->json();
}
}

View File

@ -0,0 +1,270 @@
<?php
namespace app\controller\manager;
use app\model\Log;
use app\model\Menu;
use app\model\Menu as MenuModel;
use app\model\Role as RoleModel;
use app\model\Rules;
use app\repository\CmsRepository;
use Exception;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\exception\ValidateException;
use think\facade\Db;
use think\response\Json;
use think\response\View;
/**
* 角色管理
*
* Class Role
* @package app\controller\manager
*/
class Role extends Base
{
/**
* 删除
*
* @return Json
*/
public function del(): Json
{
if ($this->request->isPost()) {
$ids = input('post.ids/a', []);
if (empty($ids)) {
$ids[] = input('post.id/d');
}
RoleModel::deleteByIds($ids);
Log::write(get_class().'Del', 'del', '涉及到的ID为'.implode(',', $ids));
return $this->json();
}
return $this->json(4001, '非法请求!');
}
/**
* 编辑
*
* @return Json|View
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
* @throws Exception
*/
public function edit()
{
$id = input('id/d', 0);
if (!$info = RoleModel::findById($id)) {
return $this->json(4001, '记录不存在');
}
if ($this->request->isPost()) {
$item = input('post.');
$validate = $this->validateByApi($item, [
'title' => 'require',
]);
if ($validate !== true) {
return $validate;
}
try {
$info->save($item);
return $this->json();
} catch (ValidateException $e) {
return $this->json(4001, $e->getError());
}
}
$this->data['item'] = $info;
return $this->view();
}
/**
* 单个字段编辑
*
* @return Json
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
* @throws Exception
*/
public function modify(): Json
{
if ($this->request->isPost()) {
$item = input('post.');
$validate = $this->validateByApi($item, [
'field' => 'require',
'value' => 'require',
]);
if ($validate !== true) {
return $validate;
}
if (!$info = RoleModel::findById($item['id'])) {
return $this->json(4001, '记录不存在');
}
$update = [$item['field'] => $item['value']];
try {
$info->save($update);
return $this->json();
} catch (ValidateException $e) {
return $this->json(4001, $e->getError());
}
}
return $this->json(4000, '非法请求');
}
/**
* 添加
*
* @return Json|View
* @throws Exception
*/
public function add()
{
if ($this->request->isPost()) {
$item = input('post.');
$validate = $this->validateByApi($item, [
'title' => 'require',
]);
if ($validate !== true) {
return $validate;
}
try {
RoleModel::create($item);
return $this->json();
} catch (ValidateException $e) {
return $this->json(4001, $e->getError());
}
}
return $this->view();
}
/**
* 角色权限
*
* @return Json|View
* @throws Exception
*/
public function rule()
{
$id = input('id/d', 0);
if (!$item = RoleModel::findById($id)) {
return $this->json(4001, '记录不存在');
}
if ($this->request->isPost()) {
$ids = input('post.ids');
$roleUpdate = $ids;//角色更新数据
$ids = explode(',', $ids);
Db::startTrans();
try {
//查询角色已有权限
$hasRules = Rules::where('ptype', 'p')->where('v0', $id)->select()->toArray();
//角色最新权限列表
$currentRules = MenuModel::where('id', 'in', $ids)->field('name')->select()->toArray();
foreach ($currentRules as &$rule) {
$route = explode(':', $rule['name']);
$v1 = $route[0];
$v2 = $route[1] ?? 'index';
$rule['ptype'] = 'p';
$rule['v0'] = $id;
$rule['v1'] = $v1;
$rule['v2'] = $v2;
}
foreach ($hasRules as $k => $has) {
foreach ($currentRules as $m => $current) {
if ($has['ptype'] == $current['ptype'] && $has['v0'] == $current['v0'] && $has['v1'] == $current['v1'] && $has['v2'] == $current['v2']) {
unset($currentRules[$m]);//删除当前权限列表已存在的 currentRules剩下的就是需要添加的记录
unset($hasRules[$k]);//删除已有权限中存在的 hasRules剩下的就是需要删除的记录
}
}
}
$insert = $currentRules;//需要添加的数据
$delete = $hasRules;//需要删除的数据
$deleteIds = array_column($delete, 'id');//需要删除的ID
(new Rules())->saveAll($insert);
(new Rules())->where('id', 'in', $deleteIds)->delete();
cache('tauthz', null);//权限缓存清空
$item->save(['rules' => $roleUpdate]);
Db::commit();
return $this->json();
} catch (ValidateException $e) {
Db::rollback();
return $this->json(4001, $e->getError());
}
}
$selected = explode(',', $item['rules']);
$this->data['authJson'] = $this->authJson($selected);
$this->data['item'] = $item;
return $this->view();
}
/**
* 构造json数据
*
* @param array $selected
* @return false|string
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
private function authJson(array $selected = [])
{
$menus = Menu::field("id,pid,title,sort")
->where('status', Menu::STATUS_NORMAL)
->order('sort', 'desc')
->order('id', 'asc')
->select()->toArray();
foreach ($menus as $k => $m) {
$menus[$k]['checked'] = in_array($m['id'], $selected);
$menus[$k]['open'] = true;
}
$menus = CmsRepository::getInstance()->buildMenuChild(0, $menus);
return json_encode($menus, JSON_UNESCAPED_UNICODE);
}
/**
* 列表
*
* @return View|Json
* @throws Exception
*/
public function index()
{
if ($this->request->isPost()) {
$page = input('page/d', 1);
$limit = input('size/d', 20);
$items = RoleModel::findList([], [], $page, $limit, function ($q) {
return $q->order('sort', 'desc')->order('id', 'asc');
});
return $this->json(0, '操作成功', $items);
}
return $this->view();
}
}

View File

@ -0,0 +1,277 @@
<?php
namespace app\controller\manager;
use app\model\Log;
use app\repository\CmsRepository;
use app\repository\OperationRepository;
use app\validate\Slide as VSlide;
use Exception;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\exception\ValidateException;
use think\response\Json;
use think\response\View;
class Slide extends Base
{
/**
* 编辑
*
* @return Json|View
* @throws DbException
* @throws ModelNotFoundException
*/
public function edit()
{
$id = $this->request->param('id/d', 0);
if (!$slide = OperationRepository::getInstance()->findSlideById($id)) {
return $this->json(4001, '数据不存在');
}
if ($this->request->isPost()) {
$item = input('post.item/a');
$validate = new VSlide();
if (!$validate->scene('slide')->check($item)) {
return $this->json(4002, $validate->getError());
}
unset($item['id']);
OperationRepository::getInstance()->updateSlide($item, $id);
return $this->json();
}
$this->data['item'] = $slide;
$this->data['positionsJson'] = $this->xmSelectPositionsJson([$slide['position']]);
$this->data['id'] = $id;
return $this->view();
}
/**
* 添加
*
* @return View|Json
*/
public function add()
{
$repo = OperationRepository::getInstance();
if ($this->request->isPost()) {
$item = input('post.item/a');
$validate = new VSlide();
if (!$validate->scene('slide')->check($item)) {
return $this->json(4002, $validate->getError());
}
$item['type'] = $item['type'] ?? 'img';
$repo->createSlide($item);
return $this->json();
}
$this->data['positionsJson'] = $this->xmSelectPositionsJson();
return $this->view();
}
/**
* 轮播图列表
*
* @return Json|View
* @throws Exception
*/
public function index()
{
$repo = OperationRepository::getInstance();
if ($this->request->isPost()) {
$position = $this->request->param('position/s', '');
$page = $this->request->param('page/d', 1);
$size = $this->request->param('size/d', 30);
$whereMap = [];
$orders = ['sort'=>'asc'];
if (!empty($position)) {
$whereMap[] = ['position', '=', $position];
}
$list = $repo->slideList($whereMap, [], $page, $size, null, $orders);
return $this->json(0, 'success', $list);
}
$this->data['positions'] = $repo->slidePositions();
return $this->view();
}
/**
* 排序
* @return Json
*/
public function sort()
{
if (!$this->request->isPost()) {
return $this->json(4000, '非法请求');
}
try {
$id = $this->request->param('id/d', 0);
$sort = $this->request->param('sort/d', 0);
OperationRepository::getInstance()->updateSlide(['sort'=>$sort], $id);
} catch (Exception $e) {
return $this->json(4001, '排序失败');
}
return $this->json();
}
/**
* 删除
* @return Json
*/
public function del()
{
if (!$this->request->isPost()) {
return $this->json(4000, '非法请求');
}
$ids = $this->request->param('ids/a', []);
if (empty($ids)) {
$ids[] = $this->request->param('id/d', 0);
$ids = array_filter($ids);
}
if (count($ids)) {
OperationRepository::getInstance()->deleteSlides($ids);
Log::write(get_class(), 'del', '删除了轮播图涉及到的ID为'.implode(',', $ids));
}
return $this->json();
}
/**
* 显示位置下拉选项数据
*
* @param array $selected
* @param array $disabled
* @return false|string
*/
private function xmSelectPositionsJson(array $selected = [], array $disabled = [])
{
$positionList = OperationRepository::getInstance()->slidePositions();
foreach ($positionList as $k => $item) {
$positionList[$k]['selected'] = in_array($item['key'], $selected);
$positionList[$k]['disabled'] = in_array($item['key'], $disabled);
}
$positionList = CmsRepository::getInstance()->handleSelectedList($positionList);
return json_encode($positionList, JSON_UNESCAPED_UNICODE);
}
/**
* 轮播图显示位置管理
*
*/
public function position()
{
$repo = OperationRepository::getInstance();
if ($this->request->isPost()) {
$list = $repo->slidePositionList([], [], 1, 0);
return $this->json(0, 'success', $list);
}
return $this->view();
}
/**
* 添加显示位置信息
*
* @return Json|View
*/
public function addPosition()
{
$repo = OperationRepository::getInstance();
if ($this->request->isPost()) {
$item = input('post.item/a');
try {
$this->validate($item, [
'title|标题' => 'max:250',
'key|位置标识' => 'require|max:100|alphaDash'
]);
} catch (ValidateException $e) {
return $this->json(4002, $e->getError());
}
if ($repo->slidePositionExists($item['key'])) {
return $this->json(4003, '当前位置标识已存在!');
}
$repo->createSlidePosition($item);
return $this->json();
}
return $this->view();
}
/**
* 编辑显示位置信息
*
* @return Json|View
* @throws DbException
* @throws ModelNotFoundException
* @throws DataNotFoundException
*/
public function editPosition()
{
$id = $this->request->param('id/d', 0);
if (!$position = OperationRepository::getInstance()->findSlidePositionById($id)) {
return $this->json(4001, '数据不存在');
}
if ($this->request->isPost()) {
$item = input('post.item/a');
try {
$this->validate($item, [
'title|标题' => 'max:250'
]);
} catch (ValidateException $e) {
return $this->json(4002, $e->getError());
}
unset($item['id']);
unset($item['key']);
OperationRepository::getInstance()->updateSlidePosition($item, $id);
return $this->json();
}
$this->data['item'] = $position;
$this->data['id'] = $id;
return $this->view();
}
/**
* 删除显示位置信息
* @return Json
*/
public function delPosition()
{
if (!$this->request->isPost()) {
return $this->json(4000, '非法请求');
}
$ids = $this->request->param('ids/a', []);
if (empty($ids)) {
$ids[] = $this->request->param('id/d', 0);
$ids = array_filter($ids);
}
if (count($ids)) {
OperationRepository::getInstance()->deleteSlidePositions($ids);
Log::write(get_class(), 'delPosition', '删除了轮播显示位置涉及到的ID为'.implode(',', $ids));
}
return $this->json();
}
}

View File

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

View File

@ -0,0 +1,241 @@
<?php
namespace app\controller\manager;
use app\exception\RepositoryException;
use app\service\Image;
use app\model\{System, File};
use app\validate\Upload as VUpload;
use think\facade\{Filesystem, Config, Lang};
use think\Image as TImage;
use app\controller\BaseController;
class Upload extends BaseController
{
protected $noNeedLogin = ['video', 'file', 'image', 'wangImage'];
private $isCompress = true;
private $validate;
private $uploadPath;
private $videoUploadPath;
private $uploadPathIsWritable = 0;
private $videoUploadPathIsWritable = 0;
private $DIRECTORY_SEPARATOR = "/";
public function __construct()
{
$system = System::getSystem();
if (!empty($system)) {
$this->isCompress = $system['compress'] ?? true;
}
$this->validate = new VUpload();
$this->uploadPath = Config::get('filesystem.disks.local.url');
$this->videoUploadPath = Config::get('filesystem.disks.video.url');
if (is_writable(app()->getRootPath().'public'.$this->uploadPath)) {
$this->uploadPathIsWritable = 1;
}
if (is_writable(app()->getRootPath().'public'.$this->videoUploadPath)) {
$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');//自定义目录
$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('上传文件夹需要写入权限');
}
$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');
try {
$res = $this->uploadImage($image);
return $this->json(0, '上传成功', $res);
} catch (RepositoryException $e) {
return $this->json(1, $e->getMessage());
}
}
//富文本编辑器商城图片
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)) {
$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);
}
public function uploadImage($image)
{
// 字段名 image-image避免冲突 layui组件自动生成的隐藏file input框中name容易重名冲突
$md5 = $image->md5();//文件md5
$type = request()->param('type/s', '');
$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'];
$return['thumb_src'] = Image::getThumb($fileItem['src'], 100, 100, TImage::THUMB_SCALING);
return $return;
}
$info = @getimagesize($image->getPathname());
$width = $info[0] ?? 0;
$height = $info[1] ?? 0;
// 海报限制大小 750 * 1334
switch ($type) {
case 'porter':
if ($width != 750 || $height != 1334) {
return $this->json(1, '海报背景图尺寸大小固定为750 * 1334');
}
break;
}
// 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;
$src = Filesystem::putFile($datePath, $image, 'uniqid');
$src = $this->uploadPath . $this->DIRECTORY_SEPARATOR . $src;
$suffix = strtolower($image->getOriginalExtension());
if ($suffix == 'gif') {
$return['thumb_src'] = $src; //TODO获取GIF缩略图
} else {
$return['thumb_src'] = Image::getThumb($src, 100, 100, TImage::THUMB_SCALING); //上传返回缩略图宽度为100
}
$return['src'] = $src;
if ($this->isCompress) {
Image::resize($src);
}
File::add($image, $src, $md5); //加入上传文件表
} catch (\Exception $e) {
throw new RepositoryException($e->getMessage());
}
return $return;
} else {
$errorMsg = Lang::get($this->validate->getError());
throw new RepositoryException($errorMsg);
}
}
}

View File

@ -0,0 +1,282 @@
<?php
namespace app\controller\manager\account;
use app\controller\manager\Base;
use app\exception\RepositoryException;
use app\model\AccountTag;
use app\model\Account as AccountModel;
use app\model\Order;
use app\repository\AccountRepository;
use app\repository\OrderRepository;
use app\service\Math;
use Exception;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\exception\ValidateException;
use think\facade\Db;
use think\facade\Log;
use think\response\Json;
use think\response\Redirect;
use think\response\View;
/**
* 用户管理
*
* Class Footmarks
* @package app\controller\manager
*/
class Index extends Base
{
protected $noNeedLogin = ['getAccountList'];
/**
* 详情
*
* @return View
* @throws RepositoryException
*/
public function detail(): View
{
$id = input('id/d', 0);
$item = AccountRepository::getInstance()->findById($id);
$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;
$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;
});
$item['total_price'] = Math::fen2Yuan($totalPrice);
$item['order_num'] = $orderNum;
$item['order_score_num'] = $orderScoreNum;
$item['order_newest'] = $consumption->toArray()[0] ?? [];
$this->data['item'] = $item;
return $this->view();
}
/**
* 编辑
*
* @return Redirect|Json|View
* @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];
try {
$items = AccountRepository::getInstance()->getAndHandleAccountList($search, [], $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['tagList'] = AccountTag::getTags();
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 {
AccountRepository::getInstance()->setTagByBatch($ids, $tags);
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 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 = [];
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, '非法请求');
}
}

View File

@ -0,0 +1,173 @@
<?php
namespace app\controller\manager\account;
use app\controller\manager\Base;
use app\model\AccountTag as AccountTagModel;
use Exception;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\exception\ValidateException;
use think\response\Json;
use think\response\View;
/**
* 客户标签
*
* Class Tag
* @package app\controller\manager\account
*/
class Tag extends Base
{
/**
* 删除
*
* @return Json
*/
public function del(): Json
{
if ($this->request->isPost()) {
$ids = input('post.ids/a', []);
if (empty($ids)) {
$ids[] = input('post.id/d');
}
AccountTagModel::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 = AccountTagModel::findById($id)) {
return $this->json(4001, '记录不存在');
}
if ($this->request->isPost()) {
$item = input('post.');
$validate = $this->validateByApi($item, [
'name' => '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 = AccountTagModel::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, [
'name' => 'require',
]);
if ($validate !== true) {
return $validate;
}
$tag = AccountTagModel::findOne(["name" => $item["name"]]);
if (!empty($tag)) {
return $this->json(0, "success", ["id" => $tag["id"]]);
}
try {
$item['created_at'] = date('Y-m-d H:i:s');
$id = AccountTagModel::insertGetId($item);
return $this->json(0, "success", ["id" => $id]);
} 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 = AccountTagModel::findList([], [], $page, $limit, function ($q) {
return $q->order('sort', 'desc')->order('id', 'asc');
});
return $this->json(0, '操作成功', $items);
}
return $this->view();
}
}

View File

@ -0,0 +1,494 @@
<?php
namespace app\controller\manager\mall;
use app\controller\manager\Base;
use app\exception\RepositoryException;
use app\model\Config;
use app\model\Log;
use app\model\Sku;
use app\model\Spu as SpuModel;
use app\model\SpuActivity;
use app\repository\SpuRepository;
use app\service\Math;
use Exception;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\exception\ValidateException;
use think\facade\Db;
use think\response\Json;
use think\response\View;
class Activity extends Base
{
protected $noNeedLogin = ['getOrder', 'getOrderGroupList'];
/**
* 列表
*
* @throws Exception
*/
public function index()
{
if ($this->request->isPost()) {
$params = input();
$params['type'] = SpuModel::TYPE_GROUP_BUY;
$params['type'] = SpuModel::TYPE_GROUP_BUY;
$res = $this->spuList($params);
return $this->json(0, 'success', $res);
}
$this->data['statusList'] = SpuModel::statusTextList();
$this->data['spuTypeList'] = SpuModel::spuTypeTextList();
$this->data['type'] = SpuModel::TYPE_GROUP_BUY;
$this->data['mpPath'] = Config::MINI_PATH_SPU_INFO;
return $this->view();
}
/**
* 拼团
*
* @return Json|View
*/
public function groupMake()
{
if ($this->request->isPost()) {
$params = input();
$params['type'] = SpuModel::TYPE_GROUP_MAKE;
$res = $this->spuList($params);
return $this->json(0, 'success', $res);
}
$this->data['statusList'] = SpuModel::statusTextList();
$this->data['spuTypeList'] = SpuModel::spuTypeTextList();
$this->data['type'] = SpuModel::TYPE_GROUP_MAKE;
$this->data['url'] = '/manager/mall/activity/group-make';
$this->data['mpPath'] = Config::MINI_PATH_SPU_INFO;
return $this->view('/manager/mall/activity/index');
}
/**
* 限时促销
*
* @return Json|View
*/
public function limitTime()
{
if ($this->request->isPost()) {
$params = input();
$params['type'] = SpuModel::TYPE_LIMIT_TIME;
$res = $this->spuList($params);
return $this->json(0, 'success', $res);
}
$this->data['statusList'] = SpuModel::statusTextList();
$this->data['spuTypeList'] = SpuModel::spuTypeTextList();
$this->data['type'] = SpuModel::TYPE_LIMIT_TIME;
$this->data['url'] = '/manager/mall/activity/limit-time';
$this->data['mpPath'] = Config::MINI_PATH_SPU_INFO;
return $this->view('/manager/mall/activity/index');
}
protected function spuList(array $params): array
{
$res = SpuRepository::getInstance()->activityList($params);
$res['list'] = $res['list']->each(function ($item) {
$status = '';
$now = date('Y-m-d H:i:s');
if ($item['activity_begin_at'] > $now) {
$status = '未开始';
}
if ($item['activity_begin_at'] < $now && $now < $item['activity_end_at']) {
$status = '进行中';
}
if ($item['is_activity_history'] == SpuModel::COMMON_ON || $item['activity_end_at'] < $now) {
$status = '已结束';
}
$item->status_text = $status;
});
return $res;
}
/**
* @throws ModelNotFoundException
* @throws DbException
* @throws DataNotFoundException
* @throws Exception
*/
public function add()
{
$repo = SpuRepository::getInstance();
$type = input('type/s', SpuModel::TYPE_GROUP_BUY);
if (!in_array($type, SpuModel::activityList())) {
if ($this->request->isPost()) {
return $this->json(4000, '活动类型错误');
} else {
return $this->error('活动类型错误');
}
}
if ($this->request->isPost()) {
$item = $this->request->param('item/a', []);
$sku = $this->request->param('sku/a', []);
$spuId = $this->request->param('spu_id/d', 0);
$rules = [
'cover|活动封面' => 'require',
'activity_begin_at|开始时间' => 'require|date',
'activity_end_at|结束时间' => 'require|date',
];
$validate = $this->validateByApi($item, $rules);
if ($validate !== true) {
return $validate;
}
$item['is_score'] = SpuModel::COMMON_OFF;
$item['is_activity'] = SpuModel::COMMON_ON;
$item['activity_type'] = $type;
try {
foreach ($sku as &$val) {
if (isset($val['id'])) {
unset($val['id']);
}
}
$this->checkSku($sku);
$repo->addActivity($type, $item, $spuId, $sku);
} catch (RepositoryException $e) {
return $this->json(4002, $e->getMessage());
} catch (Exception $e) {
}
return $this->json();
}
$this->data['type'] = $type;
$this->data['limitList'] = SpuModel::limitList();
$this->data['limitFields'] = SpuRepository::getInstance()->activityFields($type);
return $this->view();
}
/**
* @throws DataNotFoundException
* @throws ModelNotFoundException
* @throws DbException
*/
public function edit()
{
$id = $this->request->param('id/d', 0);
$skuList = Sku::where('spu_activity_id', $id)
->where('enable', Sku::COMMON_ON)
->order('sort', 'asc')
->order('id', 'asc')->select()->toArray();
if ($this->request->isPost()) {
$item = $this->request->param('item/a', []);
$sku = $this->request->param('sku/a', []);
try {
$this->checkSku($sku);
SpuRepository::getInstance()->editActivity($id, $item, $sku, $skuList);
} catch (RepositoryException $e) {
return $this->json(4002, $e->getMessage());
} catch (Exception $e) {
}
return $this->json();
}
$spuActivity = SpuActivity::findById($id);
if (empty($spuActivity)) {
return $this->json(4000, '没有相关的商品记录!');
}
foreach ($skuList as &$val) {
$val['price'] = Math::fen2Yuan($val['price']);
$val['original_price'] = Math::fen2Yuan($val['original_price']);
}
$this->data['item'] = $spuActivity;
$this->data['skuList'] = $skuList;
$this->data['limitList'] = SpuModel::limitList();
$this->data['limitFields'] = SpuRepository::getInstance()->activityFields($spuActivity['activity_type']);
$this->data['id'] = $id;
return $this->view();
}
/**
* @throws RepositoryException
* @throws Exception
*/
protected function checkSku(array $sku)
{
// sku验证
if (empty($sku)) {
throw new RepositoryException('规格信息不能为空');
}
foreach ($sku as $k) {
$validate = $this->validateByApi($k, [
'title' => 'require',
'stock' => 'require|number|gt:0',
'price' => 'number',
'score' => 'number',
'is_default' => 'require|in:0,1',
]);
if ($validate !== true) {
return $validate;
}
}
}
/**
* @throws ModelNotFoundException
* @throws DbException
* @throws DataNotFoundException
* @throws Exception
*/
public function modify()
{
if (!$this->request->isPost()) {
return $this->json(4000, '非法请求');
}
$item = input('post.');
$validate = $this->validateByApi($item, [
'field' => 'require',
'value' => 'require',
]);
if ($validate !== true) {
return $validate;
}
if (!$info = SpuActivity::findById($item['id'])) {
return $this->json(4001, '记录不存在');
}
$update = [$item['field'] => $item['value']];
try {
$info->save($update);
if ($item['field'] == 'is_check' && $info['is_activity_history'] == SpuModel::COMMON_OFF) {
SpuModel::where('id', $info['spu_id'])->save(['is_check' => $item['value']]);
}
return $this->json();
} catch (ValidateException $e) {
return $this->json(4001, $e->getError());
} catch (Exception $e) {
return $this->json(5000, '修改失败');
}
}
/**
* 立即结束-还原为普通商品
*
* @return Json
*/
public function end(): Json
{
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);
}
try {
if (count($ids)) {
SpuRepository::getInstance()->restore($ids);
Log::write(get_class(), 'end', '还原了活动商品涉及到的ID为'.implode(',', $ids));
}
} catch (RepositoryException $e) {
return $this->json(4001, $e->getMessage());
}
return $this->json();
}
/**
* 删除-还原为普通商品且隐藏
*
* @return Json
*/
public function del(): Json
{
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);
}
try {
if (count($ids)) {
SpuRepository::getInstance()->restore($ids, false, true);
// (new SpuActivity())->whereIn('id', $ids)->save(['deleted_at' => date('Y-m-d H:i:s')]);
Log::write(get_class(), 'del', '还原了活动商品涉及到的ID为'.implode(',', $ids));
}
} catch (RepositoryException $e) {
return $this->json(4001, $e->getMessage());
}
return $this->json();
}
/**
* 活动商品详情
*
* @return View
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
* @throws Exception
*/
public function info(): View
{
$id = input('id/d', 0);
$item = SpuActivity::findById($id);
$status = '';
$now = date('Y-m-d H:i:s');
if ($item['activity_begin_at'] > $now) {
$status = '未开始';
}
if ($item['activity_begin_at'] < $now && $now < $item['activity_end_at']) {
$status = '进行中';
}
if ($item['is_activity_history'] == SpuModel::COMMON_ON || $item['activity_end_at'] < $now) {
$status = '已结束';
}
$item->status_text = $status;
if ($item->activity_type == SpuModel::TYPE_GROUP_MAKE) {
//拼团
$orderList = SpuRepository::getInstance()->getActivityGroupList($id);
} else {
// 团购 促销
$orderList = SpuRepository::getInstance()->getActivityOrderList($id);
}
$totalPriceList = $orderList['list']->where('is_paid', SpuActivity::COMMON_ON)->column('total_price');
$codingList = $orderList['list']->column('coding');
$accountList = $orderList['list']->column('account_id');
$codingCount = count(array_unique($codingList));
$accountCount = count(array_unique($accountList));
$totalPrice = array_sum($totalPriceList);
$item->total_money = Math::fen2Yuan($totalPrice) ?? 0;
$item->order_count = $codingCount;
$item->account_count = $accountCount;
$this->data['item'] = $item;
$this->data['statusList'] = \app\model\Order::statusTextList();
$this->data['activityList'] = SpuModel::activityTextList();
$this->data['limitList'] = SpuModel::limitList();//限购天数展示
return $this->view();
}
/**
* 获取活动商品的订单列表
*
* @return Json
* @throws Exception
*/
public function getOrder(): Json
{
$id = input('id/d', 0);
$page = input('page/d', 1);
$size = input('size/d', 20);
$res = SpuRepository::getInstance()->getActivityOrderList($id, $page, $size);
return $this->json(0, '操作成功', $res);
}
/**
* 获取拼团列表
*
* @return Json
* @throws Exception
*/
public function getOrderGroupList(): Json
{
$id = input('id/d', 0);
$page = input('page/d', 1);
$size = input('size/d', 20);
$res = SpuRepository::getInstance()->getActivityGroupList($id, $page, $size);
return $this->json(0, '操作成功', $res);
}
/**
* 批量审核
*
* @return View|Json
* @throws Exception
*/
public function check()
{
$id = input('id/s', '');
if ($this->request->isPost()) {
$ids = input('ids/s');
$check = input('is_check/d');
if (!in_array($check, [SpuActivity::COMMON_ON, SpuActivity::COMMON_OFF])) {
return $this->json(4001, '请选择是否展示');
}
$ids = explode(',', $ids);
Db::startTrans();
try {
(new SpuActivity())->whereIn('id', $ids)->save(['is_check' => $check]);
// 活动商品对应的普通商品审核状态同步
$spuIds = SpuActivity::whereid('id', $ids)
->where('is_activity_history', SpuActivity::COMMON_OFF)
->column('spu_id');
SpuModel::whereIn('id', $spuIds)->save(['is_check' => $check]);
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();
}
}

View File

@ -0,0 +1,225 @@
<?php
namespace app\controller\manager\mall;
use app\controller\manager\Base;
use app\repository\CmsRepository;
use app\model\mall\Category as CategoryModel;
use app\validate\MenuValidate;
use Exception;
use think\facade\Db;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\exception\ValidateException;
use think\response\Json;
use think\response\View;
/**
* 商品分类管理
*
* Class Menu
* @package app\controller\manager\mall
*/
class Category extends Base
{
/**
* 删除
*
* @return Json
*/
public function del(): Json
{
if ($this->request->isPost()) {
$ids = input('post.ids/a', []);
if (empty($ids)) {
$ids[] = input('post.id/d');
}
if (CategoryModel::hasChildrenByIds($ids)) {
return $this->json(4002, '待删除数据存在子数据');
}
if (CategoryModel::hasContentByIds($ids)) {
return $this->json(4002, '待删除数据存在内容文章');
}
CategoryModel::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 = CategoryModel::findById($id)) {
return $this->json(4001, '记录不存在');
}
if ($this->request->isPost()) {
$item = input('post.');
$validate = $this->validateByApi($item, [
'pid|父级分类' => 'require|number',
'title|标题' => 'require|max:100',
// 'name|标识' => 'unique:spu_category,name,'.$info['id'] ?? 0,
'description|描述' => 'max:255',
]);
if ($validate !== true) {
return $validate;
}
Db::startTrans();
try {
$oldPath = $info['path'] ?? '';
$item['path'] = CategoryModel::getPath($item['pid']);
$info->save($item);
//刷新所有路径
$oldPath = $oldPath.','.$id;
$newPath = $item['path'].','.$id;
if ($oldPath != $newPath) {
CategoryModel::refreshPath();
}
Db::commit();
return $this->json();
} catch (ValidateException $e) {
Db::rollback();
return $this->json(4001, $e->getError());
}
}
$disabled = CategoryModel::getAllChildrenIds($id);
$disabled[] = $id;
$this->data['jsonList'] = $this->categoryJson([$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 = CategoryModel::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, [
'pid|父级分类' => 'require|number',
'title|标题' => 'require|max:100',
//'name|标识' => 'require|unique:spu_category',
'description|描述' => 'max:255',
]);
if ($validate !== true) {
return $validate;
}
Db::startTrans();
try {
$item['path'] = CategoryModel::getPath($item['pid']);
(new CategoryModel())->create($item);
Db::commit();
return $this->json();
} catch (ValidateException $e) {
Db::rollback();
return $this->json(4001, $e->getError());
}
}
$this->data['jsonList'] = $this->categoryJson();
return $this->view();
}
/**
* 列表
*
* @return Json|View
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function index()
{
if ($this->request->isPost()) {
$menus = CategoryModel::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 = CategoryModel::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);
}
}

View File

@ -0,0 +1,183 @@
<?php
namespace app\controller\manager\mall;
use app\controller\manager\Base;
use app\model\Account;
use app\model\AccountCoupon as AccountCouponModel;
use app\model\Coupon as CouponModel;
use app\model\Log;
use app\service\Math;
use Exception;
use think\exception\ValidateException;
use think\response\Json;
class Coupon extends Base
{
/**
* @throws Exception
*/
public function index()
{
if ($this->request->isPost()) {
$page = $this->request->param('page/d', 1);
$size = $this->request->param('size/d', 20);
$startAt = $this->request->param('begin_at/s', '');
$endAt = $this->request->param('end_at/s', '');
$whereMap[] = ['deleted_at', '=', null];
$order = ['sort' => 'desc', 'id' => 'desc'];
if (!empty($startAt) && strtotime($startAt)) {
$whereMap[] = ['begin_at', '>=', $startAt];
}
if (!empty($endAt) && strtotime($endAt)) {
$whereMap[] = ['end_at', '<=', $endAt];
}
$res = CouponModel::findList($whereMap, [], $page, $size, null, $order);
$res['list'] = $res['list']->each(function ($item) {
$item->type_text = CouponModel::typeTextList()[$item->type] ?? '';
});
return $this->json(0, 'success', $res);
}
return $this->view();
}
public function add()
{
if ($this->request->isPost()) {
$item = $this->request->param('item/a', []);
try {
$rules = [
'name|优惠券名称' => 'require',
'condition|消费额度' => 'float',
'amount|优惠额度' => 'float',
'total|发行总量' => 'require|integer|gt:0',
'begin_at|开始时间' => 'require|date',
'end_at|截止时间' => 'require|date',
];
$validate = $this->validateByApi($item, $rules);
if ($validate !== true) {
return $validate;
}
if ($item['end_at'] <= $item['begin_at']) {
throw new ValidateException('优惠期限的截止时间必须大于开始时间!');
}
if ($item['is_home'] == CouponModel::COMMON_ON && empty($item['cover'])) {
throw new ValidateException('推荐首页时必传封面图');
}
$data = arrayKeysFilter($item, [
'name', 'cover', 'type', 'condition', 'amount', 'begin_at', 'end_at', 'is_home', 'total'
]);
$data['created_at'] = date('Y-m-d H:i:s');
$data['remain'] = $data['total'];
CouponModel::create($data);
} catch (ValidateException $e) {
return $this->json(4001, $e->getMessage());
} catch (Exception $e) {
return $this->json(5000, '新增优惠卷失败!');
}
return $this->json();
}
$this->data['typeList'] = CouponModel::typeTextList();
return $this->view();
}
/**
* @throws Exception
*/
public function modify()
{
if (!$this->request->isPost()) {
return $this->json(4000, '非法请求');
}
$item = input('post.');
$validate = $this->validateByApi($item, [
'field' => 'require',
'value' => 'require',
]);
if ($validate !== true) {
return $validate;
}
if (!$info = CouponModel::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());
} catch (Exception $e) {
return $this->json(5000, '修改失败');
}
}
/**
* 软删除
*
* @return Json
*/
public function del(): Json
{
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)) {
CouponModel::where('id', 'in', $ids)->save(['deleted_at' => date('Y-m-d H:i:s')]);
Log::write(get_class(), 'del', '删除了优惠卷信息涉及到的ID为'.implode(',', $ids));
}
return $this->json();
}
//用户持有优惠券的列表
public function accountHasList()
{
$id = input("id/d", 0);
if ($this->request->isPost()) {
$page = $this->request->param('page/d', 1);
$size = $this->request->param('size/d', 20);
$order = ['id' => 'desc'];
$res = AccountCouponModel::findList(["coupon_id" => $id], [], $page, $size, function ($q) {
return $q->with(["coupon", "account"]);
}, $order);
$res['list'] = $res['list']->each(function ($item) {
$item->type_text = CouponModel::typeTextList()[$item->type] ?? '';
$item->check_by_account = Account::findOne(["id" => $item->check_by,], ["nickname"]);
});
return $this->json(0, 'success', $res);
}
$this->data["id"] = $id;
return $this->view();
}
}

View File

@ -0,0 +1,128 @@
<?php
namespace app\controller\manager\mall;
use app\controller\manager\Base;
use app\model\Log;
use app\model\Express as ExpressModel;
use app\repository\OrderRepository;
use app\service\Math;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\response\Json;
/**
* 快递配送管理
* Class Express
* @package app\controller\manager\mall
*/
class Express extends Base
{
public function index()
{
if ($this->request->isPost()) {
$items = OrderRepository::getInstance()->allExpress();
$items->each(function ($item) {
$item->default_price = Math::fen2Yuan($item->default_price);
});
$data = [
'total' => $items->count(),
'list' => $items,
];
return $this->json(0, 'success', $data);
}
return $this->view();
}
public function add()
{
if ($this->request->isPost()) {
$data = $this->request->param('item/a', []);
$validate = $this->validateByApi($data, [
'code|快递代号' => 'require|unique:express',
]);
if ($validate !== true) {
return $validate;
}
if ($data['is_default'] == ExpressModel::COMMON_ON) {
if (ExpressModel::where('is_default', ExpressModel::COMMON_ON)->count() > 0) {
return $this->json(4001, '默认快递已存在');
}
}
OrderRepository::getInstance()->addExpress($data);
return $this->json();
}
return $this->view();
}
/**
* @throws DataNotFoundException
* @throws ModelNotFoundException
* @throws DbException
*/
public function edit()
{
$id = $this->request->param('id/d', 0);
if (!$express = ExpressModel::findById($id)) {
return $this->json(4004, '没有相关的快递公司记录');
}
if ($this->request->isPost()) {
$data = $this->request->param('item/a', []);
$validate = $this->validateByApi($data, [
'code|快递代号' => 'require|unique:express,code,'.$id,
]);
if ($validate !== true) {
return $validate;
}
if ($data['is_default'] == ExpressModel::COMMON_ON) {
if (ExpressModel::where('id', '<>', $id)->where('is_default', ExpressModel::COMMON_ON)->count() > 0) {
return $this->json(4001, '默认快递已存在');
}
}
$express->save($data);
return $this->json();
}
$express->default_price = Math::fen2Yuan($express->default_price);
$this->data['item'] = $express;
$this->data['id'] = $id;
return $this->view();
}
/**
* @return Json
*/
public function del(): Json
{
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)) {
ExpressModel::deleteByIds($ids);
Log::write(get_class(), 'del', '删除了快递记录涉及到的ID为'.implode(',', $ids));
}
return $this->json();
}
}

View File

@ -0,0 +1,609 @@
<?php
namespace app\controller\manager\mall;
use app\controller\manager\Base;
use app\exception\RepositoryException;
use app\exception\TraitException;
use app\model\OrderSku;
use app\model\Express;
use app\repository\OrderRepository;
use app\service\Math;
use Exception;
use PhpOffice\PhpSpreadsheet\IOFactory;
use \PhpOffice\PhpSpreadsheet\Shared\Date as PDate;
use PhpOffice\PhpSpreadsheet\Style\Alignment; //设置对齐方式
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\exception\ValidateException;
use think\facade\Db;
use app\model\Order as OrderModel;
use think\facade\Filesystem;
use think\response\Json;
use think\response\View;
class Order extends Base
{
protected $noNeedLogin = ['getOrderSpu', 'checkSku', 'exportOrderList', 'importOrder'];
/**
* @throws Exception
*/
public function index()
{
if ($this->request->isPost()) {
$page = $this->request->param('page/d', 1);
$size = $this->request->param('size/d', 20);
$searchParams = $this->request->param('searchParams/a', []);
$res = $this->getOrderList($searchParams, $page, $size);
return $this->json(0, 'success', $res);
}
$this->data['statusList'] = OrderModel::statusTextList();
return $this->view();
}
/**
* 积分订单
*
* @throws Exception
*/
public function score()
{
if ($this->request->isPost()) {
$page = $this->request->param('page/d', 1);
$size = $this->request->param('size/d', 20);
$searchParams = $this->request->param('searchParams/a', []);
$searchParams['is_score'] = OrderModel::COMMON_ON;
$res = $this->getOrderList($searchParams, $page, $size);
return $this->json(0, 'success', $res);
}
$this->data['statusList'] = OrderModel::statusTextList();
$this->data['dataUrl'] = '/manager/mall/order/score';
$this->data['isScore'] = OrderModel::COMMON_ON;
return $this->view('manager/mall/order/index');
}
/**
* @param array $searchParams
* @param int $page
* @param int $size
* @return array
* @throws Exception
*/
protected function getOrderList(array $searchParams, int $page, int $size): array
{
// 主动触发:自动检测处理过期订单
// OrderRepository::getInstance()->autoCheckInvalidOrders();
$status = $searchParams['status'] ?? '';
$startAt = $searchParams['start_at'] ?? '';
$endAt = $searchParams['end_at'] ?? '';
$hasVirtual = $searchParams['has_virtual'] ?? '';
$isScore = $searchParams['is_score'] ?? 0;
$whereMap = [];
$order = ['id' => 'desc'];
$whereMap[] = ['is_score', '=', $isScore];
if (!empty($status) && in_array($status, array_keys(OrderRepository::getInstance()->orderStatusTextList()))) {
$whereMap[] = ['status', '=', $status];
}
if ($hasVirtual != '') {
$whereMap[] = ['has_virtual', '=', $hasVirtual];
}
if (!empty($startAt) && strtotime($startAt)) {
$whereMap[] = ['created_at', '>= TIME', $startAt];
}
if (!empty($endAt) && strtotime($endAt)) {
$whereMap[] = ['created_at', '<= TIME', $endAt];
}
if (isset($searchParams['coding']) && !empty($searchParams['coding'])) {
$whereMap[] = ['coding', '=', $searchParams['coding']];
}
$res = OrderModel::findList($whereMap, [], $page, $size, function ($q) {
return $q->with(['account']);
}, $order);
$res['list'] = $res['list']->each(function ($item) {
$item->status_text = OrderModel::statusTextList()[$item->status] ?? '';
$item->price = Math::fen2Yuan($item->price);
$item->nickname = $item->account->nickname ?? '';
$item->real_name = $item->account->real_name ?? '';
$item->pick_self_text = $item->pick_self == 1 ? '自提' : '邮寄';
$item->is_score_text = $item->is_score == 1 ? '是' : '否';
});
return $res;
}
/**
* 获取订单商品列表
*
* @return Json
* @throws Exception
*/
public function getOrderSpu(): Json
{
$coding = input('coding/s', '');
$where = [];
$where[] = ['order_coding', '=', $coding];
$res = OrderSku::findList($where);
$res['list'] = $res['list']->each(function ($item) {
$item->activity_text = \app\model\Spu::activityTextList()[$item->activity_type] ?? '';
});
return $this->json(0, '操作成功', $res);
}
/**
* 核销订单
*
* @return Json
* @throws RepositoryException
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function checkSku(): Json
{
$coding = input('coding/s', '');
$id = input('id/d', 0);
$num = input('num/d', 1);
try {
$checkUserInfo = sprintf("ID:%d 昵称:%s", $this->auth['user_id'], $this->auth['nickname']);
OrderRepository::getInstance()->checkBase($coding, $id, $num, $checkUserInfo, OrderModel::CHECK_TYPE_BACKEND);
event('OrderSpuCheck', $coding);
return $this->json(0, '核销成功');
} catch (ValidateException $e) {
return $this->json(4001, $e->getError());
}
}
/**
* 订单发货、重发(填写快递信息)
* 已有快递记录则覆盖
*
*/
public function send()
{
$repo = OrderRepository::getInstance();
$orderId = $this->request->param('id/d', 0);
try {
$order = $repo->findByid($orderId, [], function ($q) {
return $q->with([
'account', 'skus' => function ($qr) {
$qr->with(['sku']);
}
]);
});
if (empty($order)) {
return $this->json(4004, '没有相关的订单信息!');
}
} catch (RepositoryException $e) {
return $this->json(4004, '没有相关的订单信息!');
}
if ($this->request->isPost()) {
$data = [
'express_number' => trim($this->request->param('express_number/s', '')),
'express_id' => $this->request->param('express_id/d', 0),
'business_remarks' => trim($this->request->param('business_remarks/s', ''))
];
Db::startTrans();
try {
$nowDate = date('Y-m-d H:i:s');
if ($order->pick_self == 1) {
$sendData = [
'shipped_at' => $nowDate,
'business_remarks' => $data['business_remarks'],
];
if (empty($order['shipped_at'])) {
$sendData['shipped_at'] = $nowDate;
}
$sendData['status'] = OrderModel::STATUS_SHIPPED;
} else {
$this->validate($data, [
'express_number|快递单号' => 'require|max:100',
'express_id|快递公司' => 'require|gt:0',
'business_remarks|卖家备注' => 'max:2000',
]);
if (!$express = Express::findById($data['express_id'])) {
throw new ValidateException('没有相关的快递配置信息');
}
if (!in_array($order['status'], [OrderModel::STATUS_PAID, OrderModel::STATUS_SHIPPED])) {
throw new ValidateException('该订单当前状态不支持配送信息的录入');
}
$sendData = [
'express_number' => $data['express_number'],
'express_code' => $express['code'],
'express_name' => $express['name'],
'business_remarks' => $data['business_remarks'],
];
if (empty($order['shipped_at']) || $order['express_number'] != $data['express_number']) {
$sendData['shipped_at'] = $nowDate;
}
if ($order['status'] == OrderModel::STATUS_PAID) {
$sendData['status'] = OrderModel::STATUS_SHIPPED;
}
}
$order->save($sendData);
Db::commit();
} catch (ValidateException $e) {
Db::rollback();
return $this->json(4001, $e->getMessage());
} catch (TraitException | RepositoryException $e) {
Db::rollback();
return $this->json(4002, '配送信息保存失败!');
}
return $this->json();
}
$this->data['id'] = $orderId;
$this->data['item'] = $order;
$this->data['expressJson'] = $this->handleXmExpress([$order->express_code]);
$this->data['statusList'] = OrderModel::statusTextList();
return $this->view();
}
/**
* 订单详情
*
* @return Json|View
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function info()
{
$id = input('id/d', 0);
$item = OrderModel::findById($id, [], function ($q) {
return $q->with(['account', 'skus']);
});
if ($this->request->isPost()) {
$businessRemarks = trim(input('business_remarks/s', ''));
$item->save(['business_remarks' => $businessRemarks]);
return $this->json();
}
$this->data['id'] = $id;
$this->data['item'] = $item;
$this->data['statusList'] = OrderModel::statusTextList();
return $this->view();
}
private function handleXmOrderStatus(array $selected = [], array $disabled = [])
{
$list = [];
$statusList = OrderRepository::getInstance()->orderStatusTextList();
foreach ($statusList as $key => $val) {
$list[] = [
'name' => $val,
'value' => $key,
'selected' => in_array($key, $selected),
'disabled' => in_array($key, $disabled),
];
}
return json_encode($list, JSON_UNESCAPED_UNICODE);
}
private function handleXmExpress(array $selected = [], array $disabled = [])
{
$selected = array_filter($selected);
$disabled = array_filter($disabled);
$list = [];
$items = OrderRepository::getInstance()->allExpress();
foreach ($items as $item) {
$list[] = [
'name' => $item->name,
'value' => $item->id,
'selected' => in_array($item->code, $selected),
'disabled' => in_array($item->code, $disabled),
];
}
return json_encode($list, JSON_UNESCAPED_UNICODE);
}
/**
* 导出订单列表
*
* @return Json
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function exportOrderList(): Json
{
if ($this->request->isPost()) {
$ids = input('ids/a', []);
$type = input('type/s', 'all');
$isScore = input('is_score/d', 0);
$where = [];
$where[] = ['status', '=', OrderModel::STATUS_PAID];
$where[] = ['pick_self', '=', OrderModel::COMMON_OFF];
if ($type !== 'all') {
//获取指定订单列表
$where[] = ['id', 'in', $ids];
}
$where[] = ['is_score', '=', $isScore];
$list = OrderModel::where($where)->order('id', 'desc')->select();
$result['header'] = [
'订单编号', '订单状态', '下单时间', '付款时间', '配送方式', '收货信息', '买家选择快递', '快递编码', '快递单号', '注1.若快递公司变更,请一定更改快递编码 2.上传物流时请直接在快递单号一栏填写,切勿变更列的顺序'
];
if ($list->isEmpty()) {
return $this->json(200, '没有相应订单');
}
$result['data'] = [];
$statusList = OrderModel::statusTextList();
$list->each(function ($item) use (&$result, $statusList) {
$arr = [
"'".$item['coding'],//订单编号
$statusList[$item['status']] ?? '其他',//订单状态
"'".$item['created_at'],//下单时间
"'".$item['paid_at'],//付款时间
$item['pick_self'] == 1 ? '自提' : '邮寄',//配送方式
$item['pick_self'] == 1 ? '自提' : $item['address'],//收货信息
$item['pick_self'] == 1 ? '自提' : $item['express_name'],//买家选择的快递公司
"'".$item['pick_self'] == 1 ? '' : $item['express_code'],//买家选择等快递编码
'',//快递单号
];
$result['data'][] = $arr;
});
return $this->json(0, 'success', $result);
}
return $this->json(4000, '请求错误');
}
/**
* 导入物流
*
* @return Json
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function importOrder(): Json
{
if ($this->request->isPost()) {
$fileSrc = input('file/s', '');
$path = public_path().$fileSrc;
if (!file_exists($path)) {
return $this->json(4000, '文件地址错误'.$path);
}
$inputFileType = IOFactory::identify($path);
$reader = IOFactory::createReader($inputFileType);
$spreadsheet = $reader->load($path);
$expressList = Express::column('name', 'code');//快递公司列表
$expressCodeList = array_keys($expressList);//快递编号列表
$sheetData = $spreadsheet->getActiveSheet()->removeRow(1)->toArray(null, true, true, true);
$orderCodingList = [];
$update = [];
$now = date('Y-m-d H:i:s');
foreach ($sheetData as $key => $data) {
$currentKey = $key + 1;
if (!isset($data['A']) || empty($data['A'])) {
return $this->json(4000, '第'.$currentKey.'行记录订单号不存在');
}
if (!isset($data['H']) || !in_array($data['H'], $expressCodeList)) {
return $this->json(4000, '第'.$currentKey.'行快递公司编号不存在');
}
if (!isset($data['I']) || empty($data['I'])) {
continue;
// 快递单号不存在则不更新
// return $this->json(4000, '第'.$currentKey.'行快递单号不存在');
}
$orderCoding = $data['A'];
$expressCode = $data['H'];
$expressNumber = $data['I'];
$orderCodingList[] = $orderCoding;
$update[$orderCoding] = [
'coding' => $orderCoding,
'status' => OrderModel::STATUS_SHIPPED,
'express_code' => $expressCode,
'express_number' => $expressNumber,
'shipped_at' => $now,
'express_name' => $expressList[$expressCode] ?? '',
];
}
$orderList = OrderModel::whereIn('coding', $orderCodingList)->column('id,coding,status', 'coding');
$notUpdateStatus = [];//不更新状态都记录
$existCodings = array_keys($orderList);//数据库存在的订单编号
$coding2Id = [];
// 仅修改快递信息,不修改订单状态的列表 即仅状态=付款的订单修改状态
foreach ($orderList as $order) {
if ($order['status'] != OrderModel::STATUS_PAID) {
$notUpdateStatus[] = $order['coding'];
}
$coding2Id[$order['coding']] = $order['id'];
}
// 存在差异的订单号(数据库不存在的)
$diff = array_diff($orderCodingList, $existCodings);
foreach ($update as $coding => $item) {
if (in_array($coding, $diff) || !isset($coding2Id[$coding])) {
unset($update[$coding]);
continue;
}
$update[$coding]['id'] = $coding2Id[$coding];
if (in_array($coding, $notUpdateStatus)) {
unset($update[$coding]['status']);
}
}
(new OrderModel())->saveAll($update);
return $this->json(0, '操作成功', ['count' => count($update)]);
}
return $this->json(4000, '请求错误');
}
/**
* 修改订单里面的sku信息
*
* @return Json
* @throws Exception
*/
public function editOrderSku(): Json
{
if ($this->request->isPost()) {
$item = input('post.');
$validate = $this->validateByApi($item, [
'id' => 'require',
'field' => 'require',
'value' => 'require',
]);
if ($validate !== true) {
return $validate;
}
$orderRep = OrderRepository::getInstance();
if (!$orderSkuInfo = $orderRep->getOrderSku($item['id'])) {
return $this->json(4001, '订单商品记录不存在');
}
if (!$order = OrderModel::findOne([["coding","=",$orderSkuInfo["order_coding"]]])) {
return $this->json(4001, '订单记录不存在');
}
Db::startTrans();
try {
$orderSkuInfo[$item['field']] = $item['value'];
//如果是 修改的字段时 数量或者单价 就要调整
$orderSkuInfo["subtotal"] = $orderSkuInfo["num"] * $orderSkuInfo["price"];
$orderSkuInfo->save();
$orderOriginalPrice = $orderRep->getOrderOriginalPrice($orderSkuInfo["order_coding"],$orderSkuInfo["coding"]);
$orderOriginalPrice += $orderSkuInfo["subtotal"];
$order->save(["original_price"=>$orderOriginalPrice]);
Db::commit();
return $this->json(0,"修改成功",["original_price"=>$orderOriginalPrice]);
} catch (ValidateException $e) {
return $this->json(4001, $e->getError());
}
}
return $this->json(4000, '非法请求');
}
/**
* 导出订单信息
* */
public function exportOrderInfo()
{
$id = input("id/d");
$order = OrderModel::findById($id, [], function ($q) {
return $q->with([ 'skus']);
});
// 水平居中对齐
$styleArray = [
'alignment' => [
'horizontal' => Alignment::HORIZONTAL_CENTER,
'vertical' => Alignment::VERTICAL_CENTER,
],
];
// Create new Spreadsheet object
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$spreadsheet->getActiveSheet()->setTitle('订单信息');
//设置默认行高。
$sheet->getDefaultRowDimension()->setRowHeight(25);
//设置A1单元格
$sheet->getStyle('A1')->applyFromArray($styleArray);
$setColWidth = [
"A"=>"30",
"B"=>"30",
"C"=>"30",
"D"=>"30",
"E"=>"30",
];
foreach ($setColWidth as $key=>$citem){
$spreadsheet->getActiveSheet()->getColumnDimension($key)->setWidth($citem);
}
//合并单元格
$spreadsheet->getActiveSheet()->mergeCells('A1:B1');
$sheet->setCellValue('A1', "日期:".$order->created_at);
//第一种保存方式
$writer = new Xlsx($spreadsheet);
//保存的路径可自行设置
$path = public_path()."storage/order_excel/" ;
if(!is_writable($path)){
return $this->json("上传文件夹需要写入权限");
}
$path.=date("Ymd")."/";
if(!is_dir($path)){
mkdir($path);
}
$fileName = $order->contacts . "_" . $order->phone . "_" . $order->coding . ".xlsx";
$filepath = $path. $fileName;
$writer->save($filepath);
}
}

View File

@ -0,0 +1,280 @@
<?php
namespace app\controller\manager\mall;
use app\controller\manager\Base;
use app\controller\manager\Upload;
use app\exception\RepositoryException;
use app\model\mall\SpuLimitTime;
use app\model\sku\SpecParam;
use app\model\sku\SpecValue;
use app\model\sku\SpuType;
use app\model\Spu;
use app\model\Sku as SkuModel;
use Exception;
use think\facade\Db;
use think\response\Json;
class Sku extends Base
{
protected $noNeedRight = [
'spuTypeData', 'attrSpecData', 'specValueCreate',
'specCreate', 'specValueDelete', 'specDelete', 'upload', 'skuData'
];
/**
* 获取SKU数据
*
* @throws Exception
*/
public function skuData(): Json
{
$productId = input('product_id', 0);
if ($productId == 0) {
return $this->json(200, 'success', []);
}
$spu = Spu::findById($productId, ['id', 'activity_id', 'activity_type', 'multi_spec']);
$res = [];
switch ($spu['activity_type']) {
case SpuLimitTime::TYPE:
$where = ['spu_activity_id' => $spu['activity_id'], 'type' => $spu['activity_type']];
break;
default:
$where = ['spu_id' => $productId];
}
if ($spu['multi_spec'] > 0) {
$list = SkuModel::where($where)->whereNull('deleted_at')->whereNotNull('indexes')->select();
foreach ($list as $item) {
$key = $item['indexes'];
$res[sprintf("skus[%s][%s]", $key, 'id')] = $item['id'];
$res[sprintf("skus[%s][%s]", $key, 'stock')] = $item['stock'];
$res[sprintf("skus[%s][%s]", $key, 'original_price')] = $item['original_price'];
$res[sprintf("skus[%s][%s]", $key, 'price')] = $item['price'];
$res[sprintf("skus[%s][%s]", $key, 'picture')] = $item['picture'];
$res[sprintf("skus[%s][%s]", $key, 'is_default')] = $item['is_default'];
$res[sprintf("skus[%s][%s]", $key, 'coding')] = $item['coding'];
$res[sprintf("skus[%s][%s]", $key, 'status')] = $item['status'];
}
} else {
$item = SkuModel::where($where)->where('is_default', 1)->find();
$res['skus[id]'] = $item['id'];
$res['skus[stock]'] = $item['stock'];
$res['skus[original_price]'] = $item['original_price'];
$res['skus[price]'] = $item['price'];
$res['skus[coding]'] = $item['coding'];
$res['skus[status]'] = $item['status'];
$res['skus[is_default]'] = $item['is_default'];
}
return $this->json(200, 'success', $res);
}
/**
* 获取商品类型列表
*
* @throws Exception
*/
public function spuTypeData(): Json
{
$list = SpuType::where('status', SpuType::COMMON_ON)
->order('sort', 'desc')
->order('id', 'asc')
->field('id,title')
->select();
return $this->json(200, 'success', $list);
}
/**
* 获取商品类型下的规格和属性
*
* @throws Exception
*/
public function attrSpecData(): Json
{
$spuTypeId = input('product_type_id/d');
$productId = input('product_id/d', 0);//商品ID
$spuInfo = Spu::findById($productId, ['id', 'activity_id', 'activity_type', 'multi_spec', 'spec']);
$selectSpec = [];
if ($spuInfo) {
switch ($spuInfo['activity_type']) {
case SpuLimitTime::TYPE:
$selectSpec = SpuLimitTime::where('id', $spuInfo['activity_id'])->value('spec');
break;
default:
$selectSpec = $spuInfo['spec'];
}
$selectSpec = json_decode($selectSpec, true);
}
$specIds = SpuType::where('id', $spuTypeId)->value('spec_ids');
$specIdArr = explode(',', $specIds);
$list = SpecParam::whereIn('id', $specIdArr)
->where('status', SpecParam::COMMON_ON)
->with([
'specValue' => function ($q) {
$q->where('status', SpecValue::COMMON_ON)->order('sort', 'desc')->order('id', 'asc');
}
])
->order('sort', 'desc')
->order('id', 'asc')
->field('id,title')
->select();
$spec = [];//规格
$attr = [];//属性
$list->each(function ($item) use (&$spec, $selectSpec) {
$arr = [];
$arr['id'] = (string) $item->id;
$arr['title'] = $item->title;
$arr['options'] = $item->specValue->each(function ($spec) {
$spec->id = (string) $spec->id;
unset($spec->sort);
unset($spec->spec_id);
unset($spec->status);
unset($spec->created_at);
unset($spec->updated_at);
});
$arr['value'] = $selectSpec[$item->id] ?? [];
$spec[] = $arr;
});
return $this->json(200, 'success', ['spec' => $spec, 'attribute' => $attr]);
}
/**
* 创建规格值
*
* @throws Exception
*/
public function specValueCreate(): Json
{
$params = input('post.');
$rules = [
'spec_id|规格ID' => 'require|number',
'title|规格值' => 'require',
];
$validate = $this->validateByApi($params, $rules);
if ($validate !== true) {
return $validate;
}
$count = SpecValue::where('spec_id', $params['spec_id'])
->where('title', $params['title'])
->where('status', SpecValue::COMMON_ON)
->count();
if ($count > 0) {
return $this->json(4000, '规格值已存在');
}
$value = SpecValue::create([
'spec_id' => $params['spec_id'],
'title' => $params['title'],
]);
return $this->json(200, 'success', ['id' => $value->id]);
}
/**
* 创建规格
*
* @return bool|Json
* @throws Exception
*/
public function specCreate()
{
$params = input('post.');
$rules = [
'product_type_id|商品类型' => 'require|number',
'title|规格名称' => 'require',
];
$validate = $this->validateByApi($params, $rules);
if ($validate !== true) {
return $validate;
}
$count = SpecParam::where('spu_type_id', $params['product_type_id'])
->where('title', $params['title'])
->where('status', SpecParam::COMMON_ON)
->count();
if ($count > 0) {
return $this->json(4000, '规格名称已存在');
}
$param = SpecParam::create([
'spu_type_id' => $params['product_type_id'],
'title' => $params['title'],
]);
$specIds = SpecParam::where('spu_type_id', $params['product_type_id'])->column('id');
// 更新商品类型
SpuType::where('id', $params['product_type_id'])->save(['spec_ids' => implode(',', $specIds)]);
return $this->json(200, 'success', ['id' => $param->id]);
}
/**
* 规格删除
*
* @return Json
* @throws Exception
*/
public function specDelete(): Json
{
$id = input('post.id/d', 0);
if ($id <= 0) {
return $this->json(4000, '参数错误');
}
SpecParam::where('id', $id)->save(['status' => -1]);
SpecValue::where('spec_id', $id)->save(['status' => -1]);
return $this->json(200, 'success');
}
/**
* 规格值删除
*
* @return Json
* @throws Exception
*/
public function specValueDelete(): Json
{
$id = input('post.id/d', 0);
if ($id <= 0) {
return $this->json(4000, '参数错误');
}
SpecValue::where('id', $id)->save(['status' => -1]);
return $this->json(200, 'success');
}
/**
* 上传图片
*
* @return Json
*/
public function upload(): Json
{
// 字段名 image-image避免冲突 layui组件自动生成的隐藏file input框中name容易重名冲突
$image = request()->file('file');
try {
$res = (new Upload())->uploadImage($image);
$res['url'] = $res['src'];
return $this->json(200, '上传成功', $res);
} catch (RepositoryException $e) {
return $this->json(1, $e->getMessage());
}
}
}

View File

@ -0,0 +1,488 @@
<?php
namespace app\controller\manager\mall;
use app\controller\manager\Base;
use app\exception\RepositoryException;
use app\model\Config;
use app\model\Log;
use app\model\mall\SpuCategoryPivot;
use app\model\Sku;
use app\model\Spu as SpuModel;
use app\repository\SpuRepository;
use app\service\Math;
use Exception;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\exception\ValidateException;
use think\facade\Db;
use think\response\Json;
use think\response\View;
class Spu extends Base
{
protected $noNeedRight = ['getSpuList', 'getSkuList', 'checkActivity', 'getSpu'];
/**
* @throws ModelNotFoundException
* @throws DbException
* @throws DataNotFoundException
* @throws Exception
*/
public function index()
{
if ($this->request->isPost()) {
$params = input();
$res = SpuRepository::getInstance()->list($params);
$res['list'] = $res['list']->each(function ($item) {
$text = $item->saleable ? '已上架' : '已下架';
if ($item->is_activity > 0) {
$activityText = SpuModel::activityTextList()[$item->activity_type];
$text = '<span style="color: red;">'.$text.' '.$activityText.'中</span>';
}
$item->saleable_text = $text;
});
return $this->json(0, 'success', $res);
}
$this->data['mpPath'] = Config::MINI_PATH_SPU_INFO;;
$this->data['statusList'] = SpuModel::statusTextList();
$this->data['spuTypeList'] = SpuModel::spuTypeTextList();
return $this->view();
}
/**
* @throws ModelNotFoundException
* @throws DbException
* @throws DataNotFoundException
* @throws Exception
*/
public function score()
{
if ($this->request->isPost()) {
$params = input();
$params['type'] = SpuModel::TYPE_SCORE;
$res = SpuRepository::getInstance()->list($params);
$res['list'] = $res['list']->each(function ($item) {
$text = $item->saleable ? '已上架' : '已下架';
$item->saleable_text = $text;
});
return $this->json(0, 'success', $res);
}
$this->data['mpPath'] = Config::MINI_PATH_SPU_INFO;;
$this->data['statusList'] = SpuModel::statusTextList();
$this->data['spuTypeList'] = SpuModel::spuTypeTextList();
$this->data['url'] = '/manager/mall/spu/score';
$this->data['type'] = SpuModel::TYPE_SCORE;
return $this->view('manager/mall/spu/index');
}
/**
* 添加
*
* @return Json|View
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function add()
{
$repo = SpuRepository::getInstance();
$type = input('type', SpuModel::TYPE_NORMAL);
if ($this->request->isPost()) {
$item = $this->request->param('item/a', []);
$item['is_score'] = $type == SpuModel::TYPE_SCORE;
$item['is_activity'] = in_array($type, SpuModel::activityList());
$item['activity_type'] = $type;
$multiSpec = input('post.is_attribute');// 是否多规格 0否 1是
$skus = input('post.skus');//规格数据
$item['spu_type_id'] = input('post.product_type');//商品类型ID
$item['multi_spec'] = $multiSpec;
if ($multiSpec == SpuModel::COMMON_OFF) {
$skus = [$skus];
}
try {
$this->checkData($item, $skus);
$repo->addSpu($item, $skus);
} catch (RepositoryException $e) {
return $this->json(4002, $e->getMessage());
} catch (Exception $e) {
}
return $this->json();
}
$categoryList = $repo->categoryXmSelect([], [], true);
$this->data['statusList'] = SpuModel::statusTextList();
$this->data['spuTypeList'] = SpuModel::spuTypeTextList();
$this->data['limitList'] = SpuModel::limitList();
$this->data['categoryJson'] = json_encode($categoryList, JSON_UNESCAPED_UNICODE);
$this->data['type'] = $type;
$this->data['isScore'] = $type === SpuModel::TYPE_SCORE;
return $this->view();
}
/**
* 检测商品是否处于活动
*
* @return Json
* @throws RepositoryException
*/
public function checkActivity(): Json
{
$repo = SpuRepository::getInstance();
$id = $this->request->param('id/d', 0);
$spu = $repo->findById($id);
if (empty($spu)) {
return $this->json(4000, '没有相关的商品记录!');
}
if ($spu->is_activity > 0) {
$activityText = SpuModel::activityTextList()[$spu['activity_type']] ?? '';
$msg = '该商品正在参与'.$activityText.'活动 无法进行此操作!';
return $this->json(4000, $msg);
}
return $this->json();
}
/**
* @throws DataNotFoundException
* @throws ModelNotFoundException
* @throws DbException|RepositoryException
*/
public function edit()
{
$repo = SpuRepository::getInstance();
$id = $this->request->param('id/d', 0);
$spu = $repo->findById($id);
if (empty($spu)) {
return $this->json(4000, '没有相关的商品记录!');
}
if ($spu->is_activity > 0) {
$activityText = SpuModel::activityTextList()[$spu['activity_type']] ?? '';
$msg = '该商品正在参与'.$activityText.'活动 无法进行此操作!';
if ($this->request->isPost()) {
return $this->json(4000, $msg);
} else {
return $this->error($msg);
}
}
$skuList = Sku::where('spu_id', $id)
->where('spu_activity_id', 0)
->where('enable', Sku::COMMON_ON)
->order('sort', 'asc')
->order('id', 'asc')->select()->toArray();
if ($this->request->isPost()) {
$item = $this->request->param('item/a', []);
$multiSpec = input('post.is_attribute');// 是否多规格 0否 1是
$skus = input('post.skus');//规格数据
$item['spu_type_id'] = input('post.product_type');//商品类型ID
$item['multi_spec'] = $multiSpec;
if ($multiSpec == SpuModel::COMMON_OFF) {
$skus = [$skus];
}
try {
$this->checkData($item, $skus);
$repo->editSpu($id, $item, $skus, $skuList);
} catch (RepositoryException $e) {
return $this->json(4002, $e->getMessage());
} catch (Exception $e) {
}
return $this->json();
}
$hasCategories = SpuCategoryPivot::where('spu_id', $id)->column('category_id');
$categoryList = $repo->categoryXmSelect($hasCategories, [], true);
$this->data['statusList'] = SpuModel::statusTextList();
$this->data['spuTypeList'] = SpuModel::spuTypeTextList();
$this->data['limitList'] = SpuModel::limitList();
$this->data['categoryJson'] = json_encode($categoryList, JSON_UNESCAPED_UNICODE);
$this->data['isScore'] = $spu->is_score;
$this->data['item'] = $spu;
$this->data['skuList'] = $skuList;
$this->data['id'] = $id;
return $this->view();
}
/**
* 检查数据
*
* @throws RepositoryException
* @throws Exception
*/
protected function checkData(array $item, array $skus)
{
// 基础信息验证
$validate = $this->validateByApi($item, [
'name|商品名称' => 'require|max:250',
'cover|商品封面' => 'require|max:250',
'saleable|商品状态' => 'in:0,1',
'published_at|发布日期' => 'requireIf:saleable,1|date',
'subtitle|副标题' => 'max:2000',
]);
if ($validate !== true) {
return $validate;
}
if (empty($skus)) {
throw new RepositoryException('规格信息不能为空');
}
foreach ($skus as $k) {
if ($this->validateSku($k) !== true) {
return $validate;
}
}
return $validate;
}
/**
* 获取验证结果
*
* @param $sku
* @return bool|Json
* @throws Exception
*/
protected function validateSku($sku)
{
return $this->validateByApi($sku, [
'stock' => 'require|number|gt:0',
'original_price' => 'number',
'price' => 'number',
'score' => 'number',
'is_default' => 'require|in:0,1',
]);
}
/**
* @throws ModelNotFoundException
* @throws DbException
* @throws DataNotFoundException
* @throws Exception
*/
public function modify()
{
if (!$this->request->isPost()) {
return $this->json(4000, '非法请求');
}
$item = input('post.');
$validate = $this->validateByApi($item, [
'field' => 'require',
'value' => 'require',
]);
if ($validate !== true) {
return $validate;
}
if (!$info = SpuModel::findById($item['id'])) {
return $this->json(4001, '记录不存在');
}
if ($item['field'] == 'home_display') {
$v = $item['value'] == 1 ? 'big' : 'normal';
$item['value'] = $v;
}
$update = [$item['field'] => $item['value']];
try {
$info->save($update);
return $this->json();
} catch (ValidateException $e) {
return $this->json(4001, $e->getError());
} catch (Exception $e) {
return $this->json(5000, '修改失败');
}
}
/**
* @return Json
*/
public function del(): Json
{
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);
}
try {
if (count($ids)) {
$count = SpuModel::whereIn('id', $ids)
->where('is_activity|saleable', SpuModel::COMMON_ON)
->count();
if ($count > 0) {
return $this->json(4002, '当前商品状态不可删除!');
}
SpuRepository::getInstance()->deleteSpu($ids);
Log::write(get_class(), 'del', '删除了商品信息涉及到的ID为'.implode(',', $ids));
}
} catch (RepositoryException $e) {
return $this->json(4001, $e->getMessage());
}
return $this->json();
}
/**
* 商品详情
*/
public function info()
{
$skuId = $this->request->param('id/d', 0);
$repo = SpuRepository::getInstance();
try {
$sku = $repo->findById($skuId, []);
} catch (RepositoryException $e) {
$sku = null;
}
$this->data['item'] = $sku;
return $this->view();
}
/**
* 商品状态下拉选项
*
* @param array $selected
* @param array $disabled
* @return false|string
*/
private function handleXmStatus(array $selected = [], array $disabled = [])
{
$items = SpuModel::statusTextList();
$list = [];
foreach ($items as $key => $val) {
$list[] = [
'name' => $val,
'value' => $key,
'selected' => in_array($key, $selected),
'disabled' => in_array($key, $disabled),
];
}
return json_encode($list, JSON_UNESCAPED_UNICODE);
}
/**
* @throws Exception
*/
public function getSpuList(): Json
{
if ($this->request->isPost()) {
$input = [
'type' => SpuModel::TYPE_NORMAL,
'saleable' => SpuModel::COMMON_ON,
'keyword' => input('keyword/s', '')
];
$data = SpuRepository::getInstance()->list($input);
return $this->json(0, 'success', $data);
}
return $this->json(4000, '操作错误');
}
/**
* @throws Exception
*/
public function getSpu(): Json
{
if ($this->request->isPost()) {
$id = input('id/d');
$data = SpuModel::findById($id, ['id', 'spu_type_id', 'multi_spec']);
return $this->json(0, 'success', $data);
}
return $this->json(4000, '操作错误');
}
/**
* @throws Exception
*/
public function getSkuList(): Json
{
if ($this->request->isPost()) {
$spuId = input('id/d', 0);
$data = Sku::where('spu_id', $spuId)->select();
$data = $data->each(function ($item) {
$item->original_price = Math::fen2Yuan($item->original_price);
$item->price = Math::fen2Yuan($item->price);
});
return $this->json(0, 'success', $data);
}
return $this->json(4000, '操作错误');
}
/**
* 批量审核
*
* @return View|Json
* @throws Exception
*/
public function check()
{
$id = input('id/s', '');
if ($this->request->isPost()) {
$ids = input('ids/s');
$check = input('is_check/d');
if (!in_array($check, [SpuModel::COMMON_ON, SpuModel::COMMON_OFF])) {
return $this->json(4001, '请选择是否展示');
}
$ids = explode(',', $ids);
Db::startTrans();
try {
(new SpuModel())->whereIn('id', $ids)->save(['is_check' => $check]);
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();
}
}

View File

@ -0,0 +1,355 @@
<?php
namespace app\controller\manager\mall\activity;
use app\controller\manager\Base;
use app\exception\RepositoryException;
use app\model\Log;
use app\model\mall\SpuLimitTime;
use app\model\Order;
use app\model\Sku;
use app\model\Spu as SpuModel;
use app\model\SpuActivity;
use app\repository\SpuRepository;
use app\service\Math;
use Exception;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\exception\ValidateException;
use think\facade\Db;
use think\response\Json;
use think\response\View;
class LimitTime extends Base
{
protected $noNeedLogin = ['getOrder'];
/**
* 列表
*
* @throws Exception
*/
public function index()
{
if ($this->request->isPost()) {
$keyword = input('keyword/s');
$page = input('page/d', 1);
$size = input('size/d', 20);
$where = [];
if (!empty($keyword)) {
$where[] = ['keyword', 'like', '%'.$keyword.'%'];
}
$where[] = ['deleted_at', 'exp', Db::raw('is null')];
$order = ['sort' => 'desc', 'id' => 'desc'];
$res = SpuLimitTime::fetchList($where, [], $page, $size, null, $order);
return $this->json(0, 'success', $res);
}
return $this->view();
}
/**
* @throws ModelNotFoundException
* @throws DbException
* @throws DataNotFoundException
* @throws Exception
*/
public function add()
{
if ($this->request->isPost()) {
$item = $this->request->param('item/a', []);
$skus = input('post.skus');//规格数据
$spuId = $this->request->param('spu_id/d', 0);
$rules = [
'name|活动名称' => 'require',
'cover|封面图' => 'require',
'begin_at|开始时间' => 'require|date',
'end_at|结束时间' => 'require|date',
];
$validate = $this->validateByApi($item, $rules);
if ($validate !== true) {
return $validate;
}
try {
$item['spu_id'] = $spuId;
SpuRepository::getInstance()->addLimitTime($item, $skus);
} catch (RepositoryException $e) {
return $this->json(4002, $e->getMessage());
} catch (Exception $e) {
SpuRepository::log('限时折扣创建失败', $e);
return $this->json(5000, '限时折扣创建失败');
}
return $this->json();
}
return $this->view();
}
/**
* @throws DataNotFoundException
* @throws ModelNotFoundException
* @throws DbException
*/
public function edit()
{
$id = $this->request->param('id/d', 0);
if ($this->request->isPost()) {
$item = $this->request->param('item/a', []);
$skus = $this->request->param('skus');
$rules = [
'name|活动名称' => 'require',
'cover|封面图' => 'require',
'begin_at|开始时间' => 'require|date',
'end_at|结束时间' => 'require|date',
];
$validate = $this->validateByApi($item, $rules);
if ($validate !== true) {
return $validate;
}
try {
SpuRepository::getInstance()->editLimitTime($id, $item, $skus);
} catch (RepositoryException $e) {
return $this->json(4002, $e->getMessage());
} catch (Exception $e) {
}
return $this->json();
}
$limitTime = SpuLimitTime::findById($id);
if (empty($limitTime)) {
return $this->json(4000, '没有相关记录!');
}
if (!$spuInfo = SpuModel::findById($limitTime['spu_id'], ['id', 'name', 'multi_spec', 'spu_type_id'])) {
return $this->json(4000, '基础商品不存在!');
}
$this->data['item'] = $limitTime;
$this->data['id'] = $id;
$this->data['spu'] = $spuInfo;
return $this->view();
}
/**
* @throws RepositoryException
* @throws Exception
*/
protected function checkSku(array $sku)
{
// sku验证
if (empty($sku)) {
throw new RepositoryException('规格信息不能为空');
}
foreach ($sku as $k) {
$validate = $this->validateByApi($k, [
'title' => 'require',
'stock' => 'require|number|gt:0',
'price' => 'number',
'score' => 'number',
'is_default' => 'require|in:0,1',
]);
if ($validate !== true) {
return $validate;
}
}
}
/**
* @throws ModelNotFoundException
* @throws DbException
* @throws DataNotFoundException
* @throws Exception
*/
public function modify()
{
if (!$this->request->isPost()) {
return $this->json(4000, '非法请求');
}
$item = input('post.');
$validate = $this->validateByApi($item, [
'field' => 'require',
'value' => 'require',
]);
if ($validate !== true) {
return $validate;
}
if (!$info = SpuActivity::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());
} catch (Exception $e) {
return $this->json(5000, '修改失败');
}
}
/**
* 删除-还原为普通商品且软删除
*
* @return Json
*/
public function del(): Json
{
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);
}
try {
if (count($ids)) {
SpuRepository::getInstance()->restoreLimitTime($ids, true);
Log::write(get_class(), 'del', '删除了活动商品涉及到的ID为'.implode(',', $ids));
}
} catch (Exception $e) {
return $this->json(4001, $e->getMessage());
}
return $this->json();
}
/**
* 结束-还原为普通商品 暂停
*
* @return Json
*/
public function end(): Json
{
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);
}
try {
if (count($ids)) {
SpuRepository::getInstance()->restoreLimitTime($ids);
Log::write(get_class(), 'del', '结束了活动商品涉及到的ID为'.implode(',', $ids));
}
} catch (Exception $e) {
return $this->json(4001, $e->getMessage());
}
return $this->json();
}
/**
* 重新启动
*
* @return Json
*/
public function restart(): Json
{
if (!$this->request->isPost()) {
return $this->json(4000, '非法请求');
}
try {
$ids = input('ids');
SpuRepository::getInstance()->restart($ids[0]);
Log::write(get_class(), 'restart', '重启了活动商品涉及到的ID为'.$ids[0]);
} catch (Exception $e) {
return $this->json(4001, $e->getMessage());
}
return $this->json();
}
/**
* 活动商品详情
*
* @return View
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
* @throws Exception
*/
public function info(): View
{
$id = input('id/d', 0);
$item = SpuActivity::findById($id);
$status = '';
$now = date('Y-m-d H:i:s');
if ($item['begin_at'] > $now) {
$status = '未开始';
}
if ($item['begin_at'] < $now && $now < $item['end_at']) {
$status = '进行中';
}
if ($item['status'] == 0 || $item['end_at'] < $now) {
$status = '已结束';
}
$item->status_text = $status;
$orderList = SpuRepository::getInstance()->getActivityOrderList($id);
$totalPriceList = $orderList['list']->where('is_paid', SpuActivity::COMMON_ON)->column('total_price');
$codingList = $orderList['list']->column('coding');
$accountList = $orderList['list']->column('account_id');
$codingCount = count(array_unique($codingList));
$accountCount = count(array_unique($accountList));
$totalPrice = array_sum($totalPriceList);
$item->total_money = Math::fen2Yuan($totalPrice) ?? 0;
$item->order_count = $codingCount;
$item->account_count = $accountCount;
$this->data['item'] = $item;
$this->data['statusList'] = Order::statusTextList();
$this->data['activityList'] = SpuModel::activityTextList();
$this->data['limitList'] = SpuModel::limitList();//限购天数展示
return $this->view();
}
/**
* 获取活动商品的订单列表
*
* @return Json
* @throws Exception
*/
public function getOrder(): Json
{
$id = input('id/d', 0);
$page = input('page/d', 1);
$size = input('size/d', 20);
$res = SpuRepository::getInstance()->getActivityOrderList($id, $page, $size);
return $this->json(0, '操作成功', $res);
}
}

19
app/event.php Normal file
View File

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

View File

@ -0,0 +1,10 @@
<?php
namespace app\exception;
use Exception;
class ApiRedirectException extends Exception
{
}

View File

@ -0,0 +1,8 @@
<?php
namespace app\exception;
class RepositoryException extends \Exception
{
}

View File

@ -0,0 +1,7 @@
<?php
namespace app\exception;
class TraitException extends \Exception
{
}

37
app/job/NotifySms.php Normal file
View File

@ -0,0 +1,37 @@
<?php
namespace app\job;
use app\repository\AccountRepository;
use think\queue\Job;
class NotifySms
{
public function fire(Job $job, $data){
//短信通知列表
if ($data) {
foreach ($data as $item) {
echo sprintf("短信发送成功phone:%s time:%s \n", $item['mobile'], date('Y-m-d H:i:s'));
sleep(mt_rand(1,3));
}
}
// if ($job->attempts() > 3) {
// //通过这个方法可以检查这个任务已经重试了几次了
// echo sprintf('发送短信失败过多');
// }
//如果任务执行成功后 记得删除任务不然这个任务会重复执行直到达到最大重试次数后失败后执行failed方法
$job->delete();
// 也可以重新发布这个任务
// $job->release(3); //$delay为延迟时间
}
public function failed($data){
// ...任务达到最大重试次数后,失败了
}
}

10
app/middleware.php Normal file
View File

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

View File

@ -0,0 +1,35 @@
<?php
namespace app\middleware;
use Closure;
use app\service\Jwt as JwtService;
/**
* API登录认证需要先调用JWT解析用户信息
* Class ApiLogin
* @package app\middleware
*/
class ApiLogin
{
public function handle($request, Closure $next) {
$authorization = $request->authorization ?? '';
if (empty($authorization)) {
return json(['code' => 6001, 'msg' => '请填写token']);
}
if (!JwtService::validate($authorization)) {
return json(['code' => 6001, 'msg' => 'token验证失败或已失效']);
}
$userInfo = $request->user ?? [];
if (!isset($userInfo['user_id']) || empty($userInfo['user_id'])) {
return json(['code' => 6001, 'msg' => 'token已失效']);
}
// 自定义过期时间校验。
if(isset($userInfo['expire_time']) && time() >= $userInfo['expire_time']) {
return json(['code' => 6001, 'msg' => 'token已失效']);
}
return $next($request);
}
}

50
app/middleware/Auth.php Normal file
View File

@ -0,0 +1,50 @@
<?php
namespace app\middleware;
use Closure;
use app\model\AuthRule;
use tauthz\facade\Enforcer;
use think\facade\Cache;
class Auth
{
public function handle($request, Closure $next)
{
$auth = session('auth');
if (!$auth) {
return redirect(url('manager.login/index'));
}
$module = 'manager';
$controller = unCamelize(request()->controller());
$controller = str_replace($module.'.', '', $controller);
$controller = str_replace('.', '/', $controller);//兼容多层级目录 如 /manager/test/article/index
$action = unCamelize(request()->action());
$roles = Enforcer::getRolesForUser($auth['user_id']);
// $per = Enforcer::getPermissionsForUser($roles[0]);
// var_dump($controller);
// var_dump($action);
// var_dump($roles);
// var_dump($per);
// exit;
// return $next($request);//暂时停用权限校验
// var_dump($controller);
// var_dump($action);
// var_dump(Enforcer::hasPermissionForUser(1, $controller, 'group-make'));exit;
foreach ($roles as $role) {
// TODO 关注批量权限检测是否可用
//只需要有一个角色具有权限就放通 此处第一个参数不是用户 而是 角色 此方法是检测用户|角色是否具有某个权限的公用方法
if (Enforcer::hasPermissionForUser($role, $controller, $action)) {
return $next($request);
}
}
if (request()->isAjax()) {
return json(['code' => 4001, 'msg' => '没有权限']);
} else {
return view('/manager/error/jump')->assign('msg', '很抱歉,您还没有权限,请联系管理员开通!');
}
}
}

56
app/middleware/Csrf.php Normal file
View File

@ -0,0 +1,56 @@
<?php
namespace app\middleware;
use Closure;
use think\Request;
/**
* CSRF校验
*/
class Csrf
{
public function handle(Request $request, Closure $next)
{
if($request->isPost()){
$check = $request->checkToken();
if(false === $check) {
// return $this->csrfError($request);
}
}
return $next($request);
}
protected function csrfError($request, $msg = '非法请求, 用户身份认证失败!')
{
if($request->isAjax()) {
return json(['code' => 401, 'msg' => $msg], 200);
} else {
$referer = $_SERVER['HTTP_REFERER'] ?? null;
if (empty($referer)) {
$url = '/';
} else {
$domain = $request->domain();
$urlInfo = parse_url($referer);
$scheme = $urlInfo['scheme'] ?? '';
$requestSrc = '';
if (!empty($scheme)) {
$requestSrc = $scheme.'://'.($urlInfo['host'] ?? '');
}
if($domain != $requestSrc) {
$url = '/';
} else {
$url = 'javascript:history.back(-1);';
}
}
$errorData = [
'code'=> 401,
'msg' => $msg,
'data' => [],
'wait' => 5,
'url' => $url
];
return view('error/400', $errorData);
// 返回401视图 response type has html、json、jsonp、xml、file、view、redirect
}
}
}

37
app/middleware/JWT.php Normal file
View File

@ -0,0 +1,37 @@
<?php
namespace app\middleware;
use Closure;
use app\service\Jwt as JwtService;
/**
* 根据TOKEN解析用户信息
*
* Class JWT
* @package app\middleware
*/
class JWT
{
public function handle($request, Closure $next)
{
$authorization = $request->header('Authorization');
$tokenStr = $request->param('token/s', '');
if ($authorization) {
$authorization = str_replace('Bearer ', '', $authorization);
}
//优先取header中token
$token = $authorization ?: $tokenStr;
$userInfo = [];
if (!empty($token)) {
$userInfo = JwtService::parse($token);//token中携带的简易用户信息
}
$request->user = $userInfo;
// authorization用于移交ApiLogin认证
$request->authorization = $token;
return $next($request);
}
}

26
app/middleware/Log.php Normal file
View File

@ -0,0 +1,26 @@
<?php
namespace app\middleware;
use Closure;
use think\Request;
/**
* 日志记录
*
* Class Log
* @package app\middleware
*/
class Log
{
public function handle(Request $request, Closure $next)
{
$response = $next($request);
// 添加中间件执行代码
\app\model\Log::write($request->controller(), $request->action(), $request->pathinfo(), $request->method());
return $response;
}
}

25
app/middleware/Login.php Normal file
View File

@ -0,0 +1,25 @@
<?php
namespace app\middleware;
use Closure;
use think\Request;
/**
* 前台登录验证
*
* Class Login
* @package app\middleware
*/
class Login
{
public function handle(Request $request, Closure $next)
{
if (!session('frontend_auth')) {
$url = $request->url(true);
return redirect(url('login/index').'?url='.urlencode($url));
}
return $next($request);
}
}

53
app/model/Account.php Normal file
View File

@ -0,0 +1,53 @@
<?php
namespace app\model;
use think\model\relation\BelongsToMany;
class Account extends Base
{
public const STATUS_NORMAL = 'normal'; //正常
public const STATUS_DISABLE = 'disable';//禁用
public const GENDER_UNDEFINED = 0; // 未知
public const GENDER_MALE = 1; // 男性
public const GENDER_FEMALE = 2; // 女性
// 生成个人补充信息:邀请码、用户编号
public static function onAfterInsert($item)
{
$item->invite_code = md5($item->id, false);
$item->coding = date('y').str_pad((string) $item->id, 10, '0', STR_PAD_LEFT);
$item->save();
}
/**
* 时间修改器:生日
*
* @param $value
* @return null|mixed
*/
public function setBirthdayAttr($value)
{
return empty($value) ? null : $value;
}
/**
* 时间获取器:生日
* @param $value
* @return string
*/
public function getBirthdayAttr($value)
{
return empty($value) ? '' : $value;
}
/**
* 客户标签
*
* @return BelongsToMany
*/
public function tags(): BelongsToMany
{
return $this->belongsToMany(AccountTag::class, 'account_tag_pivot', 'tag_id', 'account_id');
}
}

View File

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

124
app/model/AccountCoupon.php Normal file
View File

@ -0,0 +1,124 @@
<?php
namespace app\model;
use app\exception\RepositoryException;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\model\relation\HasOne;
class AccountCoupon extends Base
{
public const STATUS_NORMAL = 'normal';//正常
public const STATUS_USED = 'used';//已使用
public const STATUS_INVALID = 'invalid';//已失效
public const CODE_SALT = 'R5S6Y1';//优惠券加密密码盐
/**
* 状态列表
*
* @return string[]
*/
public static function statusList(): array
{
return [self::STATUS_NORMAL, self::STATUS_USED, self::STATUS_INVALID];
}
public function coupon(): HasOne
{
return $this->hasOne(Coupon::class, 'id', 'coupon_id')->bind(
[
'amount',
'name',
'cover',
'type',
'condition',
'begin_at',
'end_at',
]
);
}
public function account(): HasOne
{
return $this->hasOne(Account::class, 'id', 'account_id')->bind(
[
'nickname',
'mobile',
]
);
}
/**
* 优惠券使用
*
* @param int $accountId
* @param int $id
* @param string $orderCoding
* @param string $checkBy
* @return bool
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
* @throws RepositoryException
*/
public static function use(int $accountId, int $id, string $orderCoding = '',$checkBy =""): bool
{
$where = ['coupon_id' => $id, 'account_id' => $accountId, 'status' => AccountCoupon::STATUS_NORMAL];
if (!$accountCoupon = self::findOne($where)) {
throw new RepositoryException('您的优惠券不存在');
}
if (!empty($accountCoupon['deleted_at'])) {
throw new RepositoryException('您的优惠券不存在-2');
}
if (!$coupon = Coupon::findById($id)) {
throw new RepositoryException('此优惠券不存在或已下架');
}
if (!empty($coupon['deleted_at'])) {
throw new RepositoryException('此优惠券已下架');
}
$now = date('Y-m-d H:i:s');
if ($now < $coupon['begin_at'] || $now > $coupon['end_at']) {
throw new RepositoryException('此优惠券不在有效期');
}
if($accountCoupon["status"] != AccountCoupon::STATUS_NORMAL){
throw new RepositoryException('优惠券已使用或已失效');
}
return $accountCoupon->save([
'used_at' => $now,
'status' => self::STATUS_USED,
'order_coding' => $orderCoding,
'check_by' => $checkBy,
]);
}
/**
* 获取首页优惠券
* 没有或已领取则返回null
*
* @param int $accountId
* @return Coupon|array|\think\Model|null
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public static function homeCoupon(int $accountId)
{
if (!$coupon = Coupon::homeCoupon()) {
return null;
}
if (self::where('account_id', $accountId)->where('coupon_id', $coupon['id'])->count() > 0) {
return null;
}
return $coupon;
}
}

View File

@ -0,0 +1,99 @@
<?php
namespace app\model;
use think\model\relation\HasOne;
/**
* 积分与佣金日志
*
* Class AccountDataLog
* @package app\model
*/
class AccountDataLog extends Base
{
public const TYPE_SCORE = 'score';//积分
public const TYPE_COMMISSION = 'commission';//佣金
public const ACTION_ORDER = 'order';//订单返佣
public const ACTION_ADMIN_RECHARGE = 'admin_recharge';//后台充值
public const ACTION_ADMIN_OPERATION = 'admin_operation';//后台操作
public const ACTION_WITHDRAWAL_RETURN = 'withdrawal_return';//提现-退回
public const ACTION_WITHDRAWAL = 'withdrawal';//提现-扣除
// 积分独有
public const ACTION_SHARE_REG = 'share_reg';//分享注册
public const ACTION_SHARE_REG_CHILD = 'share_reg_child';//分享注册-下级分享时获得积分
public const ACTION_SHARE_REG_SERVICE = 'share_reg_service';//分享注册-客户分享时客服获得积分
/**
* 记录变更
*
* @param int $accountId
* @param string $name 日志名称(说明) 签到打卡、任务完成等等
* @param string $num 数量
* @param string $type 类型 目前仅 TYPE_SCORE TYPE_COMMISSION
* @param string $action 操作 如打卡=sign 任务=task 订单=order
* @param string $surplus 剩余积分或佣金
* @param string $operator 操作人
* @param int $operatorId 操作人ID
* @param string $remarks 备注
*/
public static function log(int $accountId, string $name, string $num, string $type, string $action, string $surplus, string $operator = '', int $operatorId = 0, string $remarks = '')
{
self::create([
'account_id' => $accountId,
'name' => $name,
'num' => $num,
'type' => $type,
'action' => $action,
'created_at' => date('Y-m-d H:i:s'),
'surplus' => $surplus,
'operator' => $operator,
'operator_id' => $operatorId,
'remarks' => $remarks,
]);
}
/**
* 用户
*
*/
public function account(): HasOne
{
return $this->hasOne(Account::class, 'id', 'account_id');
}
/**
* 积分操作类型
*
* @return string[]
*/
public static function scoreAction(): array
{
return [
self::ACTION_ORDER => '订单',
self::ACTION_ADMIN_RECHARGE => '后台充值',
self::ACTION_ADMIN_OPERATION => '后台操作',
self::ACTION_SHARE_REG => '分享注册',
self::ACTION_SHARE_REG_CHILD => '分享注册-下级分享',
self::ACTION_SHARE_REG_SERVICE => '分享注册-客服获得积分',
];
}
/**
* 佣金操作类型
*
* @return string[]
*/
public static function commissionAction(): array
{
return [
self::ACTION_ORDER => '订单返佣',
self::ACTION_ADMIN_RECHARGE => '后台充值',
self::ACTION_ADMIN_OPERATION => '后台操作',
self::ACTION_WITHDRAWAL_RETURN => '提现退回',
self::ACTION_WITHDRAWAL => '提现扣除',
];
}
}

380
app/model/AccountRecord.php Normal file
View File

@ -0,0 +1,380 @@
<?php
namespace app\model;
use Exception;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\facade\Db;
use think\model\relation\HasOne;
class AccountRecord extends Base
{
public const TYPE_CONTENT = 'content';//内容
public const TYPE_SPU = 'spu';//商品
public const TYPE_OTHER = 'other';//其他
public const ACTION_COLLECT = 'collect';//收藏
public const ACTION_LIKE = 'like';//点赞
public const ACTION_SHARE = 'share';//分享
public const ACTION_SEARCH = 'search';//搜索
public const ACTION_VIEW = 'view';//查看
public const ACTION_REGISTER = 'register';//注册
public const ACTION_SHARE_VIEW = 'share_view';//分享内容被查看 记录的account_id为分享人ID
public const COLLECTED = 1;
public const LIKED = 1;
/**
* 允许的类型
*
* @return string[]
*/
public static function allowTypes(): array
{
return [
self::TYPE_CONTENT, self::TYPE_OTHER, self::TYPE_SPU
];
}
/**
* 允许的操作
*
* @return string[]
*/
public static function allowActions(): array
{
return [
self::ACTION_COLLECT, self::ACTION_LIKE, self::ACTION_SHARE,
self::ACTION_REGISTER, self::ACTION_VIEW, self::ACTION_SHARE_VIEW
];
}
/**
* 内容表允许的操作
*
* @return string[]
*/
public static function archivesActions(): array
{
return [
self::ACTION_COLLECT, self::ACTION_LIKE, self::ACTION_SHARE,
self::ACTION_VIEW
];
}
/**
* 不可撤销的操作 即每次都新增记录 分享、咨询、注册
* 注意:可撤销操作需要防止撤销后重新操作导致的无限制增加统计 即刷数据 如收藏、点赞等
*
* @return string[]
*/
public static function notUndoActions(): array
{
return [self::ACTION_SHARE, self::ACTION_REGISTER, self::ACTION_VIEW];
}
/**
* 操作对应字段
*
* @return string[]
*/
public static function actionField(): array
{
return [
self::ACTION_REGISTER => 'customer',
self::ACTION_COLLECT => 'collects',
self::ACTION_LIKE => 'likes',
self::ACTION_VIEW => 'views',
self::ACTION_SHARE => 'shares',
self::ACTION_SHARE_VIEW => 'views',//分享内容被查看 也增加views
];
}
/**
* 概览操作对应字段
*
* @return string[]
*/
public static function overviewField(): array
{
return [
self::ACTION_REGISTER => 'customer',
self::ACTION_VIEW => 'views',
self::ACTION_SHARE => 'shares',
self::ACTION_SHARE_VIEW => 'views',//分享内容被查看 也增加views
];
}
/**
* 模型关联:内容文档
*/
public function archive(): HasOne
{
return $this->hasOne(Archives::class, 'id', 'relation_id');
}
/**
* 模型关联:商品
*/
public function spu(): HasOne
{
return $this->hasOne(Spu::class, 'id', 'relation_id');
}
/**
* 记录 收藏、点赞、分享、搜索、咨询、查询
*
* @param int $accountId
* @param int $relationId
* @param string $type
* @param string $action
* @return bool
* @throws ModelNotFoundException
*/
public static function record(int $accountId, string $type = self::TYPE_CONTENT, string $action = self::ACTION_COLLECT, int $relationId = 0): bool
{
Db::startTrans();
try {
$now = date('Y-m-d H:i:s');
$hadRecord = false;//是否已经记录 防重复点赞和收藏 无限计数 已记录数据不添加相应统计
$notUndoAction = self::notUndoActions();
if (in_array($action, $notUndoAction)) {
$item = null;
} else {
$item = self::where('account_id', $accountId)
->where('relation_id', $relationId)
->where('type', $type)
->where('action', $action)
->find();
}
if (!$item) {
self::create([
'account_id' => $accountId,
'relation_id' => $relationId,
'type' => $type,
'action' => $action,
'created_at' => $now,
'is_record' => self::BOOL_TRUE,
'recorded_at' => $now,
]);
} else {
if ($item['is_record'] == self::BOOL_TRUE) {
$hadRecord = true;
}
$item['is_record'] = self::BOOL_TRUE;
$item['recorded_at'] = $now;
$item['type'] = $type;
$item->save();
}
// 更新内容表
if ($type == self::TYPE_CONTENT && in_array($action, self::archivesActions())) {
if (in_array($action, $notUndoAction) || !$hadRecord) {
//不需撤销的 和 新增加的操作才计入统计
$archive = Archives::findById($relationId);
$field = self::actionField()[$action] ?? null;
if (!$field) {
throw new Exception('操作错误');
}
if ($archive) {
$archive->inc($field)->update();
}
}
}
// 更新概览数据表
Overview::renew($accountId, $action);
Db::commit();
} catch (ModelNotFoundException | Exception $e) {
Db::rollback();
throw $e;
}
return true;
}
/**
* 取消收藏|点赞
*
* @param int $accountId
* @param int $relationId
* @param string $type
* @param string $action
* @return bool
*/
public static function unRecord(int $accountId, int $relationId, string $type, string $action): bool
{
Db::startTrans();
try {
$item = self::where('account_id', $accountId)
->where('relation_id', $relationId)
->where('type', $type)
->where('action', $action)
->find();
if ($item) {
$hadUnRecord = $item['is_record'] == self::BOOL_FALSE;
$item->is_record = self::BOOL_FALSE;
$item->recorded_at = null;
$item->save();
if ($type == self::TYPE_CONTENT && !$hadUnRecord) {
$archive = Archives::findById($relationId);
$archiveUpdate = [];
if ($archive) {
switch ($action) {
case AccountRecord::ACTION_COLLECT:
$newCollects = $archive['collects'] - 1;
$newCollects = $newCollects < 0 ? 0 : $newCollects;
$archiveUpdate['collects'] = $newCollects;
break;
case AccountRecord::ACTION_LIKE:
$newLikes = $archive['likes'] - 1;
$newLikes = $newLikes < 0 ? 0 : $newLikes;
$archiveUpdate['likes'] = $newLikes;
break;
}
}
if ($archive && count($archiveUpdate)) {
Archives::updateById($relationId, $archiveUpdate);
}
}
}
Db::commit();
} catch (Exception $e) {
Db::rollback();
return false;
}
return true;
}
/**
* 获取已完成的操作
*
* @param int $accountId
* @param int $relationId
* @param string $type
* @return array
*/
public static function getDoneAction(int $accountId, int $relationId, string $type = self::TYPE_CONTENT): array
{
if ($accountId <= 0) {
return [];
}
return self::where('is_record', self::COMMON_ON)
->where('account_id', $accountId)
->where('relation_id', $relationId)
->where('type', $type)
->column('action');
}
/**
* 获取搜索记录
*
* @param int $accountId
* @param int $page
* @param int $size
* @return array|null
* @throws Exception
*/
public static function findSearch(int $accountId, int $page = 1, int $size = 20): ?array
{
if ($accountId <= 0) {
return [
'total' => 0,
'current' => $page,
'size' => $size,
'list' => [],
];
}
$where[] = ['account_id', '=', $accountId];
$where[] = ['is_record', '=', self::COMMON_ON];
return self::findList($where, ['id', 'relation_content keyword'], $page, $size, null, ['recorded_at' => 'desc']);
}
/**
* 获取搜索记录
*
* @param int $accountId
* @param string $keyword
* @return bool
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public static function saveSearch(int $accountId, string $keyword): bool
{
$item = AccountRecord::where('account_id', $accountId)->where('type', self::TYPE_CONTENT)
->where('action', self::ACTION_SEARCH)
->where('relation_content', trim($keyword))
->find();
$now = date('Y-m-d H:i:s');
if (!$item) {
AccountRecord::create([
'type' => self::TYPE_CONTENT,
'action' => self::ACTION_SEARCH,
'relation_content' => trim($keyword),
'account_id' => $accountId,
'is_record' => self::COMMON_ON,
'recorded_at' => $now,
'created_at' => $now,
]);
} else {
$item->save([
'is_record' => self::COMMON_ON,
'recorded_at' => $now
]);
}
return true;
}
/**
* 清空搜索记录
*
* @param int $accountId
* @return bool
*/
public static function clearSearch(int $accountId): bool
{
if ($accountId <= 0) {
return true;
}
$where = ['account_id' => $accountId, 'type' => self::TYPE_CONTENT, 'action' => self::ACTION_SEARCH];
AccountRecord::update(['is_record' => self::COMMON_OFF], $where);
return true;
}
/**
* 记录统计
*
* @param string $type
* @param string $action
* @param int $accountId
* @return int
*/
public static function countByAction(string $type, string $action, int $accountId = 0): int
{
$whereMap = ['ar.type' => $type, 'ar.action' => $action, 'ar.is_record' => self::BOOL_TRUE];
if ($accountId > 0) {
$whereMap['ar.account_id'] = $accountId;
}
try {
return self::alias('ar')->where($whereMap)->when($type == self::TYPE_CONTENT, function ($q) {
$q->leftJoin('archives arc', 'arc.id=ar.relation_id')->where('arc.id', '>', 0);
})->count();
} catch (Exception $e) {
return 0;
}
}
}

10
app/model/AccountTag.php Normal file
View File

@ -0,0 +1,10 @@
<?php
namespace app\model;
class AccountTag extends Base
{
public static function getTags()
{
return AccountTag::order('sort', 'desc')->order('id', 'asc')->select();
}
}

View File

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

View File

@ -0,0 +1,59 @@
<?php
namespace app\model;
use app\exception\RepositoryException;
use app\service\Math;
/**
* 用户佣金提现记录
*
* Class AccountWithdrawalCommission
* @package app\controller\manager
*/
class AccountWithdrawalCommission extends Base
{
static $status_default = 0;//待审核
static $status_success = 1;//审核通过
static $status_fail = 2;//审核失败
/**
* 客户
*/
public function account()
{
return $this->hasOne(Account::class, 'id', 'account_id');
}
/**
* 转换佣金和金额
* @param $num int 金额或者佣金 单位为分
* @param $type string commission/money 需要获得的金额或者佣金 单位分
*
* @throws RepositoryException
*/
public static function convertCommissionOrMoney(int $num, string $type)
{
\think\facade\Config::load('extra/commission_withdrawal', 'commission_withdrawal');
$config = config('commission_withdrawal')['withdrawal_proportion'] ?? [];
if (!$config) {
throw new RepositoryException("提现比例未配置,请先联系平台");
}
if ($config["commission"] <= 0 || $config["money"] <= 0) {
throw new RepositoryException("提现失败,系统错误");
}
//如果想得到佣金 或者 金额 单位分
if ($type == AccountDataLog::TYPE_COMMISSION) {
$total = Math::fen2Yuan($num) * ($config["commission"]) / ($config["money"]);
} else {
$total = $num * ($config["money"]) / ($config["commission"]);
}
return ceil($total);
}
}

58
app/model/Archives.php Normal file
View File

@ -0,0 +1,58 @@
<?php
namespace app\model;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\model\relation\HasMany;
use think\model\relation\HasOne;
class Archives extends Base
{
public const ORIGINAL_TABLE = 'bee_archives';
public const STATUS_NORMAL = 1;//正常
public const STATUS_DISABLE = 0;//禁用
/**
* 相关记录 点赞|收藏
*
* @return HasMany
*/
public function record(): HasMany
{
return $this->hasMany(AccountRecord::class, 'relation_id', 'id');
}
/**
* 是否收藏
*
* @return HasOne
*/
public function collect(): HasOne
{
return $this->hasOne(AccountRecord::class, 'relation_id', 'id')->bind(['is_collected' => 'is_record']);
}
/**
* 创建人信息
*
* @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' => 'title']);
}
}

View File

@ -0,0 +1,86 @@
<?php
namespace app\model;
use think\Collection;
use think\db\exception\DbException;
use think\db\exception\DataNotFoundException;
use think\db\exception\ModelNotFoundException;
use think\model\relation\HasOne;
class ArchivesCategory extends Base
{
public const TOP_PID = 0;
public const NAME_DIARY = 'diary'; //日记分享
/**
* 获取恒美 关于我们栏目列表
*
* @return string[]
*/
public static function aboutCategoryId(): array
{
$aboutId = self::where('name', 'about')->value('id');
return self::where('pid', $aboutId)->column('id');
}
/**
* 获取病种相关模型的栏目 模型名称为:'question', 'diary', 'effect', 'science'
*
* @return string[]
*/
public static function diseaseCategoryIds(): array
{
$modelIds = ArchivesModel::whereIn('name', ArchivesModel::models())->column('id');
return self::whereIn('model_id', $modelIds)->column('id');
}
/**
* 检测数据下 是否有子栏目
*
* @param array $ids
* @return bool
*/
public static function hasChildrenByIds(array $ids): bool
{
return self::whereIn('pid', $ids)->count() > 0;
}
/**
* 检测数据下 是否有子内容
*
* @param array $ids
* @return bool
*/
public static function hasContentByIds(array $ids): bool
{
return Archives::whereIn('category_id', $ids)->count() > 0;
}
/**
* 获取列表
*
* @return ArchivesCategory[]|array|Collection
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public static function getList()
{
return self::field('id,pid,title,name,sort,path,true as open')
->order('sort', 'desc')
->order('id', 'asc')
->select();
}
/**
* 模型
*
* @return HasOne
*/
public function model(): HasOne
{
return $this->hasOne(ArchivesModel::class, 'id', 'model_id')->bind(['model' => 'name']);
}
}

View File

@ -0,0 +1,53 @@
<?php
namespace app\model;
/**
* 文档模型
* Class ArchivesModel
* @package app\model
*/
class ArchivesModel extends Base
{
/**
* 获取恒美内容模型 问题文章|日记分享|效果模拟|科普视频
*
* @return string[]
*/
public static function models(): array
{
return ['question', 'diary', 'effect', 'science'];
}
/**
* 内容模型关联栏目
*
* @return mixed
*/
public static function categories()
{
return self::alias('am')
->leftJoin('archives_category ac', 'ac.model_id = am.id')
->field('ac.id,ac.title,ac.model_id, ac.sort,am.name as model_name')
->order('ac.sort', 'desc')
->order('ac.id', 'asc')
->whereIn('am.name', self::models())
->select();
}
/**
* 关于我们栏目
*
* @return mixed
*/
public static function about()
{
return self::alias('am')
->leftJoin('archives_category ac', 'ac.model_id = am.id')
->field('ac.id,ac.title,ac.model_id, ac.sort,am.name as model_name')
->order('ac.sort', 'desc')
->order('ac.id', 'asc')
->whereIn('ac.id', ArchivesCategory::aboutCategoryId())
->select();
}
}

View File

@ -0,0 +1,118 @@
<?php
namespace app\model;
use Exception;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\facade\Db;
/**
* 文档模型字段
* Class ArchivesModelField
* @package app\model
*/
class ArchivesModelField extends Base
{
public const VIRTUAL_YES = 1;//虚拟字段 是
public const VIRTUAL_NO = 0;//虚拟字段 否
/**
* 模型添加字段列表
*
* @param int $modelId
* @param string $modelName
* @throws Exception
*/
public static function setFieldList(int $modelId, string $modelName)
{
if (self::where('model_id', $modelId)->count() <= 0) {
$rs = Db::query("SHOW FULL COLUMNS FROM ".Archives::ORIGINAL_TABLE);
$insert = [];
foreach ($rs as $val) {
$arr = [];
$arr['model_id'] = $modelId;
$arr['model'] = $modelName;
$arr['name'] = $val['Field'];
$arr['title'] = $val['Comment'];
$arr['remark'] = $val['Comment'];
$insert[] = $arr;
}
(new self())->saveAll($insert);
}
}
/**
* 同步字段
*
* @param int $modelId
* @param string $modelName
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public static function syncFieldList(int $modelId, string $modelName)
{
$rs = Db::query("SHOW FULL COLUMNS FROM ".Archives::ORIGINAL_TABLE);
$oldFieldList = self::where('model_id', $modelId)->where('is_virtual', self::VIRTUAL_NO)->select();
$oldFields = $oldFieldList->column('name');
$newestFields = [];
foreach ($rs as $val) {
$newestFields[] = $val['Field'];
}
//待删除字段
$delete = array_diff($oldFields, $newestFields);
//待新增字段
$needInsertFields = array_diff($newestFields, $oldFields);
$insert = [];//新增字段
foreach ($rs as $val) {
if (in_array($val['Field'], $needInsertFields)) {
$arr = [];
$arr['model_id'] = $modelId;
$arr['model'] = $modelName;
$arr['name'] = $val['Field'];
$arr['title'] = $val['Comment'];
$arr['remark'] = $val['Comment'];
$insert[] = $arr;
}
}
(new self())->saveAll($insert);
(new self())->whereIn('name', $delete)->delete();
}
/**
* 各栏目[模型] 可展示字段列表
*
* @return array
*/
public static function showFieldList(): array
{
$list = self::alias('amf')
->leftJoin('archives_category ac', 'ac.model_id = amf.model_id')
->field('amf.*, ac.id as category_id, ac.title as category_title')
->where('amf.status', self::COMMON_ON)
->where('ac.id', '>', 0)
->select();
$res = [];
$list = $list->toArray();
foreach ($list as $item) {
if (!isset($res[$item['category_id']])) {
$res[$item['category_id']] = [];
}
$res[$item['category_id']][] = $item['name'];
}
return $res;
}
}

69
app/model/Attachment.php Normal file
View File

@ -0,0 +1,69 @@
<?php
namespace app\model;
use Exception;
use think\exception\ValidateException;
class Attachment extends Base
{
protected $name = 'file';
public const TYPE_DIR = 'dir';//目录
public const TYPE_IMG = 'image';//图片
public const TYPE_VIDEO = 'video';//视频
public const TYPE_FILE = 'file';//文件
public const ROOT_NAME = 'storage';//根目录名称
public const ROOT_PATH = "/".self::ROOT_NAME;//文件存放根路径
public static function list(): array
{
$where[] = ['src', 'like', '%'.self::ROOT_PATH."/"];
return self::findList($where);
}
/**
* 路径目录处理 会逐级检查路径上所有目录是否存在 不存在的目录全部创建[创建到数据库]
*
* @throws Exception
*/
public static function pathDirHandle(string $path): bool
{
$pathInfo = pathinfo($path);
$fullPath = isset($pathInfo['extension']) ? $pathInfo['dirname'] : $pathInfo['dirname']."/".$pathInfo['filename'];
// 全路径 如 /storage/dir1/dir2/dir3/dir4/ 注意前后都有/
$fullPath = $fullPath."/";
$pathArr = explode("/", trim($fullPath, "/"));
$insert = [];
$now = date('Y-m-d H:i:s');
try {
// 检测全路径上所有目录是否存在 不存在则创建
foreach ($pathArr as $k => $p) {
$currentPath = '/';
$currentArr = array_slice($pathArr, 0, $k);
if ($currentArr) {
$currentPath = "/".implode('/', $currentArr)."/";
}
if ($currentPath != '/' && self::where('path', $currentPath)->where('name', $p)->count() <= 0) {
$arr = [];
$arr['path'] = $currentPath;
$arr['name'] = $p;
$arr['created_at'] = $now;
$arr['is_dir'] = self::COMMON_ON;
$arr['type'] = self::TYPE_DIR;
$insert[] = $arr;
}
}
(new self())->saveAll($insert);
return true;
} catch (Exception $e) {
return false;
}
}
}

390
app/model/Base.php Normal file
View File

@ -0,0 +1,390 @@
<?php
namespace app\model;
use Exception;
use think\Collection;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\Model;
use think\Paginator;
class Base extends Model
{
protected $autoWriteTimestamp = false;
// 布尔值数字关系
public const BOOL_FALSE = 0;
public const BOOL_TRUE = 1;
public const COMMON_ON = 1;
public const COMMON_OFF = 0;
//根据Id列表获取列表
public static function getListByIds($ids)
{
if (count($ids) == 0 || empty($ids)) {
return [];
}
return self::where('id', 'in', $ids)->select()->toArray();
}
//根据ID获取单条数据
public static function getById($id)
{
if ($id <= 0) {
return [];
}
return self::where('id', $id)->findOrEmpty()->toArray();
}
/**
* 通过ID查找记录
*
* @param int $id
* @param array $fields
* @param callable|null $call
* @return array|Model|null
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public static function findById(int $id, array $fields = [], callable $call = null)
{
$q = self::when(!empty($fields), function ($q) use ($fields) {
$q->field($fields);
});
if ($call !== null) {
$q = $call($q);
}
return $q->find($id);
}
/**
* 查找单个记录
*
* @param array $where
* @param array $fields
* @param callable|null $call
* @return array|Model|null
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public static function findOne(array $where = [], array $fields = [], callable $call = null)
{
$q = self::when(!empty($fields), function ($q) use ($fields) {
$q->field($fields);
})->where($where);
if ($call !== null) {
$q = $call($q);
}
return $q->find();
}
//根据ID更新数据
public static function updateById($id, $data)
{
return self::where('id', $id)->update($data);
}
//根据where条件和排序获取记录
public static function getListByWhereAndOrder($where, $order, $limit = 1)
{
return self::where($where)
->order($order)
->limit($limit)
->select()
->toArray();
}
/**
* 根据ID删除数据
*
* @param int $id
* @return bool
*/
public static function deleteById(int $id): bool
{
return self::where('id', $id)->delete();
}
/**
* 根据ID列表删除数据
*
* @param array $ids
* @return bool
*/
public static function deleteByIds(array $ids): bool
{
return self::whereIn('id', $ids)->delete();
}
/**
* 排序
*
* @param int $id 调整ID
* @param string $type 本次操作类型 向上、向下
* @param int $num 移动位数
* @param string $listType 列表的排序方式 默认为降序
* @param array $where 额外条件 如限制在指定分类下 where[] = ['category_id', '=', 6]
* @return array
* @throws Exception
*/
public static function sort(int $id, string $type, int $num = 1, string $listType = 'desc', array $where = []): array
{
$res = ['code' => 0, 'msg' => 'success'];
$item = self::getById($id);
if (!$item) {
$res['code'] = 1;
$res['msg'] = '记录不存在';
return $res;
}
if ($listType == 'desc') {
if ($type == 'down') {
$where[] = ['sort', '<', $item['sort']];
$order = "sort desc";
} else {
$where[] = ['sort', '>', $item['sort']];
$order = "sort asc";
}
} else {
if ($type == 'up') {
$where[] = ['sort', '<', $item['sort']];
$order = "sort desc";
} else {
$where[] = ['sort', '>', $item['sort']];
$order = "sort asc";
}
}
$forSortItems = self::getListByWhereAndOrder($where, $order, $num);
if (!empty($forSortItems)) {
$updateData = [];
$forSortCount = count($forSortItems);
for ($i = 0; $i < $forSortCount; $i++) {
if ($i == 0) {
$updateData[] = [
'id' => $forSortItems[$i]['id'],
'sort' => $item['sort']
];
} else {
$updateData[] = [
'id' => $forSortItems[$i]['id'],
'sort' => $forSortItems[$i - 1]['sort']
];
}
}
$updateData[] = [
'id' => $item['id'],
'sort' => $forSortItems[$i - 1]['sort']
];
if (!empty($updateData)) {
$obj = new static();
$obj->saveAll($updateData);
return $res;
}
}
$res['code'] = 1;
$res['msg'] = '无需调整';
return $res;
}
/**
* 查询列表 [带分页 适用于后台]
*
* @param array $data 查询数据 格式如下
* [
* 'fields' => ['id','title','desc'],//查询字段
* 'where' => [
* ['name', 'like', '%thinkphp%'],
* ['title', 'like', '%thinkphp%'],
* ['id', '>', 0],
* ['status', '=', 1],
* ],//查询条件
* 'order' => ['order'=>'desc','id'=>'desc'],//排序
* 'size' => 50,//每页数量
* ]
* @param array $pageParams 分页参数 具体参考https://www.kancloud.cn/manual/thinkphp6_0/1037638
* @param callable|null $callback 复杂查询条件 使用闭包查询 此时建议data留空
* @return Paginator
* @throws DbException
*/
public static function findListWithPaginate(array $data = [], array $pageParams = [], callable $callback = null): Paginator
{
$q = new static();
$fields = isset($data['fields']) && !empty($data['fields']) ? $data['fields'] : [];
$where = isset($data['where']) && !empty($data['where']) ? $data['where'] : [];
$order = isset($data['order']) && !empty($data['order']) ? $data['order'] : [];
$limit = $data['size'] ?? 20;
if (count($where)) {
$q = $q->where($where);
}
if (count($fields)) {
$q = $q->field($fields);
}
if ($callback) {
$q = $callback($q);
}
if (count($order)) {
$q = $q->order($order);
}
$pageParams['list_rows'] = $limit;
return $q->paginate($pageParams);
}
/**
* 查询列表
*
* @param array $simpleWhere 简易查询条件
* @param array $fields 查询字段 []表示全部
* @param int $page 默认第一页 0不限制
* @param int $limit 限制条数 0不限制
* @param callable|null $callback 复杂的条件 使用闭包查询
* @param array $orders 键值对,排序
* @return array
* @throws Exception
*/
public static function findList(array $simpleWhere = [], array $fields = [], int $page = 1, int $limit = 0, callable $callback = null, array $orders = []): array
{
$q = new static();
$data = [
'total' => 0,
'current' => $page,
'size' => $limit,
'list' => new Collection(),
];
if (count($fields)) {
$q = $q->field($fields);
}
if (count($simpleWhere)) {
$q = $q->where($simpleWhere);
}
if ($callback) {
$q = $callback($q);
}
$data['total'] = $q->count();
if ($data['total']) {
if (count($orders)) {
$q = $q->order($orders);
}
if ($page) {
$q = $q->page($page);
}
if ($limit == 0) {
$q = $q->limit(1000);
} else {
$q = $q->limit($limit);
}
$data['list'] = $q->select();
}
return $data;
}
/**
* 获取路径
*
* @param int $pid
* @return string
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public static function getPath(int $pid): string
{
if ($pid == 0) {
$path = ',0,';
} else {
$parent = self::findById($pid);
if (empty($parent)) {
$path = ',0,';
} else {
$path = $parent['path'].$parent['id'].',';
}
}
return $path;
}
/**
* 刷新路径
*
* @param int $pid
* @param array $data 默认全部 若data不为空 至少包含[id,path, $pidField]
* @param string $pidField 父级ID字段名 默认 pid
* @throws Exception
*/
public static function refreshPath(int $pid = 0, array $data = [], string $pidField = 'pid')
{
$data = !empty($data) ? $data : self::column('id,path,'.$pidField);
$updateAllPaths = [];
self::recursionPath($pid, $data, $updateAllPaths);
(new static())->saveAll($updateAllPaths);
}
/**
* 获取递归最新路径
*
* @param int $pid
* @param array $data 全部数据 尽量传全部数据
* @param array $paths
*/
public static function recursionPath(int $pid, array $data, array &$paths = [])
{
foreach ($data as $k => $v) {
if ($pid == $v['pid']) {
$arr = [];
$arr['id'] = $v['id'];
if ($pid == 0) {
$arr['path'] = ',0,';
} else {
$arr['path'] = $paths[$v['pid']]['path'].$v['pid'].',';
}
$paths[$v['id']] = $arr;
unset($data[$k]);
self::recursionPath($v['id'], $data, $paths);
}
}
}
/**
* 获取所有后代ID[包含孙级] 仅对拥有path字段的表生效
*
* @param int $id
* @return array
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public static function getAllChildrenIds(int $id): array
{
$item = self::find($id);
if ($item && isset($item['path'])) {
$path = $item['path'].$id.',';
return self::where('path', 'like', $path.'%')->column('id');
} else {
return [];
}
}
}

23
app/model/Config.php Normal file
View File

@ -0,0 +1,23 @@
<?php
namespace app\model;
/**
* 配置表
* Class Config
* @package app\model
*/
class Config extends Base
{
// 路径都没有加前缀/ 需要的地方自行添加
// 小程序内容详情路径----新闻内容详情 包含: 关于我们模型、普通文章模型
public const MINI_PATH_ARCHIVES = 'pagesB/articleDetail/articleDetail';
// 小程序病种详情路径----病种内容详情(前端叫法) 包含: 非我们模型、普通文章模型的其他模型 如问题文章、科普视频等
public const MINI_PATH_PROBLEM = 'pagesB/problemDetail/problemDetail';
// 小程序预约列表界面
public const MINI_PATH_APPOINTMENT = 'pagesA/makeAnPppointment/makeAnPppointment';
// 小程序消息中心
public const MINI_PATH_MESSAGE_CENTER = 'pagesB/messagecenter/messagecenter';
// 商品详情
public const MINI_PATH_SPU_INFO = 'pagesB/shopDetail/shopDetail';
}

12
app/model/ConfigGroup.php Normal file
View File

@ -0,0 +1,12 @@
<?php
namespace app\model;
/**
* 配置分组表
* Class ConfigGroup
* @package app\model
*/
class ConfigGroup extends Base
{
}

75
app/model/Coupon.php Normal file
View File

@ -0,0 +1,75 @@
<?php
namespace app\model;
use think\Collection;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
/**
* 优惠券
* Class Coupon
* @package app\model
*/
class Coupon extends Base
{
public const TYPE_DISCOUNT = 'discount';//满减
public const TYPE_TASTE = 'taste';//体验
/**
* 类型文案
*
* @return string[]
*/
public static function typeTextList(): array
{
return [
self::TYPE_DISCOUNT => '满减券',
self::TYPE_TASTE => '体验券',
];
}
/**
* 可领取列表
*
* @param array $without 需要排除的ID列表
* @return Coupon[]|array|Collection
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public static function normalList(array $without = [])
{
$now = date('Y-m-d H:i:s');
return self::whereNotIn('id', $without)
->whereNull('deleted_at')
->where('begin_at', '<', $now)
->where('end_at', '>', $now)
->where('remain', '>', 0)
->order('sort', 'desc')
->order('created_at', 'desc')
->select();
}
/**
* 获取首页推荐优惠券
*
* @return Coupon|array|\think\Model|null
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public static function homeCoupon()
{
$now = date('Y-m-d H:i:s');
return self::where('is_home', self::COMMON_ON)
->whereNull('deleted_at')
->where('begin_at', '<', $now)
->where('end_at', '>', $now)
->where('remain', '>', 0)
->order('sort', 'desc')
->order('created_at', 'desc')
->find();
}
}

10
app/model/Express.php Normal file
View File

@ -0,0 +1,10 @@
<?php
namespace app\model;
class Express extends Base
{
public static function getDefaultFreight(string $code)
{
return self::where('code', $code)->value('default_price', 0);
}
}

36
app/model/Feedback.php Normal file
View File

@ -0,0 +1,36 @@
<?php
namespace app\model;
use think\model\relation\HasOne;
/**
* 投诉与意见记录
* Class Feedback
* @package app\model
*/
class Feedback extends Base
{
public const STATUS_WAITING = 0; // 待处理
public const STATUS_DONE = 1; // 已处理
/**
* 模型关联:意见分类
*
* @return HasOne
*/
public function type(): HasOne
{
return $this->hasOne(FeedbackType::class, 'id', 'type_id');
}
/**
* 模型关联:用户
*
* @return HasOne
*/
public function account(): HasOne
{
return $this->hasOne(Account::class, 'id', 'account_id');
}
}

View File

@ -0,0 +1,12 @@
<?php
namespace app\model;
/**
* 投诉与意见分类
* Class FeedbackType
* @package app\model
*/
class FeedbackType extends Base
{
}

129
app/model/File.php Normal file
View File

@ -0,0 +1,129 @@
<?php
namespace app\model;
use app\service\AliOss;
use Exception;
use think\facade\Config;
use think\Image;
class File extends Base
{
const IMG = 'image';
const VIDEO = 'video';
const FILE = 'file';
//获取文件类型
public static function getTypes()
{
return [
'image' => '图片',
'video' => '视频',
'file' => '文件'
];
}
//获取文件列表
public static function getList($type = '', $page = 1, $per = 20)
{
$limit = ($page - 1) * $per;
if ($type != '') {
if (!in_array($type, array_keys(self::getTypes()))) {
return [];
}
$items = self::where('type', $type)
->order('id desc');
} else {
$items = self::order('id desc');
}
$items = $items->limit($limit, $per)->select()->toArray();
foreach ($items as &$item) {
$item['sizeStr'] = sizeToStr($item['size']);
}
return $items;
}
//获取分页列表
public static function getListPage($type = '', $per = 20)
{
if ($type != '') {
if (!in_array($type, array_keys(self::getTypes()))) {
return [];
}
return self::where('type', $type)
->order('id desc')
->paginate([
'list_rows' => $per,
'query' => [
'type' => $type
]
], false);
} else {
return self::order('id desc')
->paginate([
'list_rows' => $per
], false);
}
}
//添加,$w_h图片尺寸大小单位像素只对type=img时有效
public static function add($file, $src, $md5, $type = 'image')
{
$realPath = public_path().ltrim($src, '/');
$oss = false;
if (is_file($realPath) && $type == 'image') {
$img = Image::open($realPath);
list($w, $h) = $img->size();
$w_h = $w.'px * '.$h.'px';
} else {
$w_h = '';
}
$now = date('Y-m-d H:i:s');
Attachment::pathDirHandle($src);
Config::load('extra/base', 'base');
$baseConfig = config('base');
if (isset($baseConfig['oss']) && $baseConfig['oss'] == 'true') {
$ossObject = AliOss::instance();
try {
$pathInfo = pathinfo($src);
$ossConfig = AliOss::config();
$bucket = $ossConfig['bucket'];
//是否存在
if (!$ossObject->doesObjectExist($bucket, ltrim($src, '/'))) {
//创建目录
$ossObject->createObjectDir($bucket, ltrim($pathInfo['dirname'], '/'));
$ossObject->uploadFile($bucket, ltrim($src, '/'), $realPath);
}
$oss = true;
} catch (Exception $e) {
\think\facade\Log::error('阿里云OSS上传文件失败 '.$e->getMessage());
}
}
// 将src中路径创建
return self::create([
'type' => $type,
'name' => $file->getOriginalName(),
'md5' => $md5,
'src' => $src,
'path' => isset(pathinfo($src)['dirname']) ? pathinfo($src)['dirname'].'/' : '',
'size' => $file->getSize(),
'suffix' => $file->getOriginalExtension(),
'mime_type' => $file->getOriginalMime(),
'created_at' => $now,
'updated_at' => $now,
'is_oss' => $oss,
'w_h' => $w_h
]);
}
//获取所有记录
public static function getAll()
{
return self::select()->toArray();
}
}

41
app/model/Log.php Normal file
View File

@ -0,0 +1,41 @@
<?php
namespace app\model;
use think\model\relation\HasOne;
class Log extends Base
{
//记录操作日志
public static function write($controller, $action, $content, $requestType = '')
{
$auth = session('auth');
return self::create([
'member_id' => $auth['user_id'] ?? 0,
'name' => $auth['username'] ?? 0,
'ip' => request()->ip(),
'create_time' => time(),
'controller' => $controller,
'request_type' => $requestType,
'action' => $action,
'content' => $content
]);
}
/**
* @return HasOne
*/
public function memberName(): HasOne
{
return $this->hasOne(Member::class, 'id', 'member_id')->bind(['operator' => 'nickname']);
}
public function getCreateTimeAttr($value)
{
if (empty($value)) {
return $value;
}
return date('Y-m-d H:i:s', $value);
}
}

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

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

26
app/model/Member.php Normal file
View File

@ -0,0 +1,26 @@
<?php
namespace app\model;
class Member extends Base
{
public const STATUS_NORMAL = 1;//正常
public const STATUS_DISABLE = 0;//禁用
public static function getList($limit = 40)
{
return self::alias('m')
->leftjoin('auth_group g', 'g.id=m.group_id')
->field('m.id,m.username,m.login_time,m.group_id,g.title')
->order('m.id', 'asc')
->paginate($limit);
}
//根据用户名获取管理账号
public static function getByUserName($username)
{
return self::where('username', trim($username))
->findOrEmpty()
->toArray();
}
}

71
app/model/Menu.php Normal file
View File

@ -0,0 +1,71 @@
<?php
namespace app\model;
use Exception;
class Menu extends Base
{
public const SHOW_YES = 1;
public const SHOW_NO = 0;
public const STATUS_NORMAL = 1;
public const STATUS_DISABLE = 0;
public const TYPE_MENU = 'menu';
public const TYPE_ACTION = 'action';
/**
* 默认操作
*
* @return string[]
*/
public static function defaultAction(): array
{
return [
'index' => '查看',
'add' => '添加',
'edit' => '编辑',
'del' => '删除',
'sort' => '排序',
'modify' => '属性设置',
];
}
/**
* 自从生成常规操作权限
*
* @param int $id
* @param string $name
* @param string $path
* @throws Exception
*/
public static function generate(int $id, string $name, string $path)
{
$actions = self::defaultAction();
$delete = [];
$insert = [];
$created = date('Y-m-d H:i:s');
foreach ($actions as $key => $action) {
$name = explode(':', $name)[0];
$delete[] = $name.':'.$key;
$arr = [];
$arr['title'] = $action;
$arr['pid'] = $id;
$arr['name'] = $name.':'.$key;
$arr['type'] = self::TYPE_ACTION;
$arr['path'] = $path.$id.',';
$arr['remark'] = sprintf("自动生成[%s][%s]操作", $name, $action);
$arr['created_at'] = $created;
$insert[] = $arr;
}
//删除已有常规操作
self::where('pid', $id)->whereIn('name', $delete)->delete();
//新增常规操作
(new self())->saveAll($insert);
}
}

51
app/model/Message.php Normal file
View File

@ -0,0 +1,51 @@
<?php
namespace app\model;
class Message extends Base
{
// 消息类型
public const TYPE_SYSTEM = 'system'; // 系统消息
public const TYPE_NOTICE = 'notice'; // 通知消息
public const TYPE_REMINDERS = 'reminders'; // 日程提醒
// 发送范围
public const TARGET_ALL = 'all'; // 所有用户
public const TARGET_PART = 'part'; // 部分用户与target_list组合查询
/**
* 消息类型文本描述
* @return string[]
*/
public static function typeTextList(): array
{
return [
self::TYPE_SYSTEM => '系统消息',
self::TYPE_NOTICE => '通知消息',
self::TYPE_REMINDERS => '日程提醒',
];
}
/**
* 消息类型文本描述
* @return string[]
*/
public static function targetTextList(): array
{
return [
self::TARGET_ALL => '所有用户',
self::TARGET_PART => '部分用户',
];
}
/**
* 获取列表
*/
public static function getList($per = 20)
{
return self::order("create_time desc")
->paginate($per);
}
}

12
app/model/MessageLog.php Normal file
View File

@ -0,0 +1,12 @@
<?php
namespace app\model;
/**
* 消息阅读记录
* Class MessageLog
* @package app\model
*/
class MessageLog extends Base
{
}

18
app/model/Model.php Normal file
View File

@ -0,0 +1,18 @@
<?php
namespace app\model;
class Model extends Base
{
//获取模型列表
public static function getList()
{
return self::order('sort asc')
->select()
->toArray();
}
public static function onAfterInsert($model)
{
$model->sort = $model->id;
$model->save();
}
}

69
app/model/Order.php Normal file
View File

@ -0,0 +1,69 @@
<?php
namespace app\model;
use think\model\relation\HasMany;
use think\model\relation\HasOne;
class Order extends Base
{
public const STATUS_ORDER_PLACED = 'order_placed';//已下单 (已付款待发货)
public const STATUS_MAKEING = 'makeing';//制作中
public const STATUS_SHIPPED = 'shipped';//已发货
public const STATUS_ARRIVED = 'arrived';//已送达
public const STATUS_COMPLETED = 'completed';//已完成 确认收获 或者 到期自动确认
public const STATUS_CANCEL = 'cancel';//已取消
public const CHECK_TYPE_FRONTEND = 'frontend';//核验类型 前端核验|线下核验
public const CHECK_TYPE_BACKEND = 'backend';//核验类型 后台核验|线上核验
public const PAY_TYPE_WECHAT = 'wechat';//微信
public const PAY_TYPE_SCORE = 'score';//积分
/**
* 核验方式列表
*
* @return string[]
*/
public static function checkTypeList(): array
{
return [self::CHECK_TYPE_FRONTEND, self::CHECK_TYPE_BACKEND];
}
/**
* 模型关联订单下的sku
* 一对多关系
*
* @return HasMany
*/
public function skus(): HasMany
{
return $this->hasMany(OrderSku::class, 'order_coding', 'coding')->whereNull('deleted_at');
}
/**
* 模型关联:下单会员
* @return HasOne
*/
public function account(): HasOne
{
return $this->hasOne(Account::class, 'id', 'account_id');
}
/**
* 订单状态描述
*/
public static function statusTextList(): array
{
return [
self::STATUS_ORDER_PLACED => '已下单',
self::STATUS_MAKEING => '制作中',
self::STATUS_SHIPPED => '已发货',
self::STATUS_ARRIVED => '已送达',
self::STATUS_COMPLETED => '已完成',
self::STATUS_CANCEL => '已取消',
];
}
}

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