feat(初始化): init

master
yin5th 2023-01-09 16:41:41 +08:00
commit ad56dae1b8
5984 changed files with 750176 additions and 0 deletions

19
.env.bak Executable 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

16
.gitignore vendored Executable file
View File

@ -0,0 +1,16 @@
.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
public/.user.ini
public/.well-known/

1
.htaccess Executable file
View File

@ -0,0 +1 @@

7
404.html Executable file
View File

@ -0,0 +1,7 @@
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx</center>
</body>
</html>

32
LICENSE.txt Executable file
View File

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

31
README.md Executable 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 Executable file
View File

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

View File

@ -0,0 +1,15 @@
<?php
namespace app\building;
interface ManagerInterface
{
// 审核录入资料
public function checkInfo();
// 审核加班
public function checkOvertime();
// 审核
}

313
app/command/Generate.php Executable file
View File

@ -0,0 +1,313 @@
<?php
declare (strict_types=1);
namespace app\command;
use think\console\Command;
use think\console\Input;
use think\console\input\Option;
use think\console\Output;
use think\facade\Db;
class Generate extends Command
{
protected string $controller;
protected string $name;
protected string $title;
protected string $namespace;//控制器命名空间
protected string $viewDir;//view目录
protected string $jsFullDir;//js完整路径目录
protected string $jsDir;//js相对路径目录
protected string $controllerFullPath;//控制器完整路径
protected string $className;//控制器类名
protected function configure()
{
// 指令配置
$this->setName('generate')
->setDescription('生成后台基础crud')
->addOption('title', 't', Option::VALUE_REQUIRED, '标题')
->addOption('name', null, Option::VALUE_REQUIRED, '权限标识')
->addOption('controller', null, Option::VALUE_REQUIRED, '控制器命名空间');
}
protected function setInfo(string $controller, string $name, string $title)
{
$this->name = $name;
$this->controller = $controller;
$this->title = $title;
$this->controllerFullPath = $this->getControllerPathName();
$this->viewDir = $this->getViewDirName();
$this->jsFullDir = $this->getJsFullDirName();
$this->jsDir = $this->getJsDirName();
$controllerPath = explode('/', trim($controller, '/'));
$this->className = $controllerPath[count($controllerPath) - 1];
$namespace = 'app\\controller\\'.implode('\\', array_slice($controllerPath, 0, count($controllerPath) - 1));
$this->namespace = $namespace;
}
protected function execute(Input $input, Output $output)
{
$title = $input->getOption('title') ? trim($input->getOption('title')) : '';
$name = $input->getOption('name') ? trim($input->getOption('name')) : '';
$controller = $input->getOption('controller') ? trim($input->getOption('controller')) : '';
$this->setInfo($controller, $name, $title);
try {
$output->writeln('开始创建');
$output->writeln('1.菜单表生成数据...');
$dbRes = $this->createDb([
'title' => $title, 'name' => $name, 'controller' => $controller
]);
$successList = [];
if ($dbRes) {
$output->info('菜单表数据生成成功!');
$successList[] = '菜单数据生成!';
} else {
$output->warning('菜单表数据生成失败');
}
$output->writeln('2.生成控制器...');
if (is_file($this->controllerFullPath)) {
$output->error('控制器: '.$controller.' 已存在!');
}
if (!is_dir(dirname($this->controllerFullPath))) {
mkdir(dirname($this->controllerFullPath), 0755, true);
}
// 生成控制器
file_put_contents($this->controllerFullPath, $this->buildController());
$output->info($this->controllerFullPath.' 控制器生成成功!');
$successList[] = '创建文件:'.$this->controllerFullPath;
$output->writeln('3.生成视图文件...');
if (!is_dir($this->viewDir)) {
mkdir($this->viewDir, 0755, true);
}
foreach (['index', 'add', 'edit'] as $action) {
$actionHtml = sprintf('%s/%s.html', $this->viewDir, $action);
if (is_file($actionHtml)) {
$output->error($actionHtml.' 视图文件已存在!');
}
file_put_contents($actionHtml, $this->buildView($action));
$output->info($actionHtml.' 文件创建成功');
$successList[] = '创建文件:'.$actionHtml;
}
$output->info('视图文件生成成功!');
$jsPath = sprintf('%s/%s.js', $this->jsFullDir, unCamelize($this->className, '_'));
$output->writeln('4.生成js文件...');
if (!is_dir($this->jsFullDir)) {
mkdir($this->jsFullDir, 0755, true);
}
file_put_contents($jsPath, $this->buildJs());
$output->info($jsPath.' js文件创建成功');
$successList[] = '创建文件:'.$jsPath;
$output->newLine();
$output->writeln('信息汇总');
// 汇总信息
foreach ($successList as $success) {
$output->info($success);
}
$output->newLine();
$output->info(sprintf('此时可访问路径查看是否创建成功 /%s/index', unCamelize($controller)));
$output->newLine();
$output->warning('请注意,控制器中 $noNeedRight 默认开启了所有权限,如需纳入权限控制,请删除并在角色中选择对应权限');
// 指令输出
$output->info('创建完成');
} catch (\Exception $e) {
$output->error($e->getMessage());
}
}
/**
* 构建控制器内容
*
* @return array|false|string|string[]
*/
protected function buildController()
{
$controllerStubPath = __DIR__.DIRECTORY_SEPARATOR.'stubs/controller.stub';
$stub = file_get_contents($controllerStubPath);
return str_replace(['{%className%}', '{%namespace%}'], [
$this->className,
$this->namespace,
], $stub);
}
/**
* 构建视图文件内容
*
* @param string $action
* @return array|false|string|string[]
*/
protected function buildView(string $action)
{
$actionStubPath = __DIR__.DIRECTORY_SEPARATOR.'stubs/'.$action.'.stub';
$stub = file_get_contents($actionStubPath);
return str_replace(['{%path%}', '{%jsPath%}'], [
'/'.unCamelize($this->controller),
$this->jsDir.'/'.unCamelize($this->className, '_').'.js',
], $stub);
}
/**
* 构建js文件内容
*
* @return false|string
*/
protected function buildJs()
{
$actionStubPath = __DIR__.DIRECTORY_SEPARATOR.'stubs/js.stub';
return file_get_contents($actionStubPath);
}
/**
* 获取控制器完整路径
*
* @return string
*/
protected function getControllerPathName(): string
{
return $this->app->getBasePath().'controller/'.ltrim(str_replace('\\', '/', $this->controller), '/').'.php';
}
/**
* 获取对应view完整目录
*
* @return string
*/
protected function getViewDirName(): string
{
return $this->app->getRootPath().'view/'.ltrim(unCamelize($this->controller, '_'), '/');
}
/**
* 获取对应js完整目录
*
* @return string
*/
protected function getJsFullDirName(): string
{
$dir = $this->app->getRootPath().'public/static/'.ltrim(unCamelize($this->controller, '_'), '/');
return str_replace('/manager/', '/manager/js/', $dir);
}
/**
* 获取对应js相对目录
*
* @return string
*/
protected function getJsDirName(): string
{
return explode('/manager/', $this->jsFullDir)[1] ?? '';
}
/**
* 自动添加菜单表数据
*
* @param array $data
* @return bool
* @throws \Exception
*/
public function createDb(array $data): bool
{
$menuTable = 'menu';
$now = date('Y-m-d H:i:s');
if (!isset($data['title']) || !isset($data['name']) || !isset($data['controller'])) {
throw new \Exception('参数错误 请添加:--title 标题 --name 权限标识如aaa/bbb --controller 控制器 如manager/aaa/Bbb');
}
if (empty($data['title']) || empty($data['name']) || empty($data['controller'])) {
throw new \Exception('参数错误 请添加:--title 标题 --name 权限标识如aaa/bbb --controller 控制器 如manager/aaa/Bbb');
}
// 检查权限标识是否已存在
$already = Db::name('menu')->where('name', $data['name'])->whereOr('title', $data['title'])->count();
if ($already) {
throw new \Exception('菜单名称或标识已存在');
}
$href = unCamelize($data['controller']);
$insertMaster = [
'pid' => 0,
'title' => $data['title'], //名称
'icon' => $data['icon'] ?? 'fa-align-justify', //菜单图标
'name' => $data['name'], //权限标识
'href' => $href.'/index',//链接
'type' => 'menu',
'is_show' => 1,
'status' => 1,
'remark' => 'command generate',
'created_at' => $now,
];
$masterId = Db::name($menuTable)->insertGetId($insertMaster);
$masterPath = sprintf(',0,%d,', $masterId);
Db::name($menuTable)->where('id', $masterId)->update([
'path' => $masterPath
]);
$insertName = trim($data['name'], '/');
$insertHref = '/'.trim($href, '/');
$actionArr = $this->actionList();
// 基于权限标识生成下级列表 默认index add del edit modify
$insertAll = [];
foreach (array_keys($actionArr) as $action) {
$insertAll[] = [
'pid' => $masterId,
'title' => $actionArr[$action]['title'], //名称
'icon' => '', //菜单图标
'name' => $insertName.':'.$action, //权限标识
'href' => $insertHref.'/'.$action,//链接
'type' => 'action',
'is_show' => 1,
'status' => 1,
'remark' => 'command generate',
'path' => $masterPath,//此时的path为负极path
'created_at' => $now,
];
}
// 插入自己菜单
Db::name($menuTable)->insertAll($insertAll);
// 批量更新自己菜单path
Db::execute("update bee_$menuTable set `path`=concat(`path`, `id`, ',') where pid=$masterId");
return true;
}
protected function actionList(): array
{
return [
'index' => ['title' => '查看'],
'add' => ['title' => '添加'],
'edit' => ['title' => '编辑'],
'del' => ['title' => '删除'],
'modify' => ['title' => '属性设置'],
'sort' => ['title' => '排序'],
];
}
}

53
app/command/stubs/add.stub Executable file
View File

@ -0,0 +1,53 @@
{layout name="manager/layout" /}
<div class="layuimini-container location-operate-page">
<div class="layuimini-main">
<div class="layui-form layuimini-form">
<div class="layui-form-item">
<label class="layui-form-label required">名称</label>
<div class="layui-input-block">
<input type="text" name="name" placeholder="请输入名称" class="layui-input" value="" maxlength="250">
</div>
</div>
<div class="layui-form-item layui-row">
<label class="layui-form-label required">封面图</label>
<div class="layui-inline layui-col-xs12 layui-col-md8">
<div class="layui-row upload-file-div">
<div class=" layui-col-xs12 layui-col-md8">
<input class="layui-input upload-file-value" name="cover" type="text" value="{$item.cover ?? ''}">
<div class="layui-form-mid layui-word-aux">图片尺寸xxx*xxx</div>
</div>
<div class="layui-col-xs12 layui-col-md3">
<span>
<button type="button" class="layui-btn layui-btn-danger upload-btn">
<i class="fa fa-upload"></i> 上传
</button>
</span>
<span>
<button type="button" class="layui-btn layui-btn-primary upload-choose-btn" data-type="image" data-multiple="false">
<i class="fa fa-list"></i> 选择
</button>
</span>
</div>
<ul class="layui-row layui-col-xs12 layui-col-md9 preview-list layui-col-space5"></ul>
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label required">内容</label>
<div class="layui-input-block">
<textarea name="content" class="layui-textarea tinymce-editor"></textarea>
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn layui-btn-normal" data-url="{%path%}/add" lay-submit lay-filter="saveBtn">确认保存</button>
</div>
</div>
</div>
</div>
</div>
<script src="__MANAGER__/{%jsPath%}?v={:mt_rand()}"></script>

178
app/command/stubs/controller.stub Executable file
View File

@ -0,0 +1,178 @@
<?php
declare (strict_types = 1);
namespace {%namespace%};
use Exception;
use app\model\Log;
use think\Collection;
use think\response\View;
use think\response\Json;
use app\controller\manager\Base;
use think\db\exception\DbException;
use think\exception\ValidateException;
use think\db\exception\DataNotFoundException;
use think\db\exception\ModelNotFoundException;
class {%className%} extends Base
{
protected $noNeedLogin = ['index', 'add', 'edit', 'del', 'modify'];
/**
* 列表
*
* @throws Exception
*/
public function index()
{
if ($this->request->isPost()) {
// $params = input();
$res = [
'total' => 5,
'current' => 1,
'size' => 10,
'list' => new Collection(),
];
$res['list'] = [
['id' => 1, 'name' => '张伟', 'status' => '111', 'sort' => 11],
['id' => 2, 'name' => '王兴龙', 'status' => '222', 'sort' => 12],
['id' => 3, 'name' => '菜盘', 'status' => '333', 'sort' => 13],
['id' => 4, 'name' => '老郑', 'status' => '444', 'sort' => 14],
['id' => 5, 'name' => '大帅比', 'status' => '555', 'sort' => 15]
];
return $this->json(0, 'success', $res);
}
return $this->view();
}
/**
* 添加
*
* @return Json|View
*/
public function add()
{
if ($this->request->isPost()) {
try {
$input = input('post.');
// 添加逻辑 TODO
return $this->json();
} catch (Exception $e) {
return $this->json(4001, '添加失败');
}
}
return $this->view();
}
/**
* 编辑
*
* @return \think\response\Json|\think\response\View
*/
public function edit()
{
$id = input('id');
//通过ID查询
$item = ['id' => 11, 'name' => '标题', 'cover' => '/xxx/xxx/xxx.jpg', 'content' => '我是大帅比'];
if (empty($item)) {
return $this->json(4000, '没有相关的商品记录!');
}
if ($this->request->isPost()) {
try {
$input = input('post.');
// 更新逻辑
return $this->json();
} catch (Exception $e) {
return $this->json(5000, $e->getMessage());
}
}
$this->data['item'] = $item;
$this->data['id'] = $id;
return $this->view();
}
/**
* 更新属性
*
* @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;
}
// 通过ID查询
if (!$info = []) {
// 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 \think\response\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)) {
//删除逻辑 TODO
Log::write(get_class(), 'del', '删除操作涉及到的ID为'.implode(',', $ids));
}
} catch (Exception $e) {
return $this->json(5000, $e->getMessage());
}
return $this->json();
}
}

54
app/command/stubs/edit.stub Executable file
View File

@ -0,0 +1,54 @@
{layout name="manager/layout" /}
<div class="layuimini-container location-operate-page">
<div class="layuimini-main">
<div class="layui-form layuimini-form">
<div class="layui-form-item">
<label class="layui-form-label required">名称</label>
<div class="layui-input-block">
<input type="text" name="name" placeholder="请输入名称" class="layui-input" value="{$item.name ?? ''}" maxlength="250">
</div>
</div>
<div class="layui-form-item layui-row">
<label class="layui-form-label required">封面图</label>
<div class="layui-inline layui-col-xs12 layui-col-md8">
<div class="layui-row upload-file-div">
<div class=" layui-col-xs12 layui-col-md8">
<input class="layui-input upload-file-value" name="cover" type="text" value="{$item.cover ?? ''}">
<div class="layui-form-mid layui-word-aux">图片尺寸xxx*xxx</div>
</div>
<div class="layui-col-xs12 layui-col-md3">
<span>
<button type="button" class="layui-btn layui-btn-danger upload-btn">
<i class="fa fa-upload"></i> 上传
</button>
</span>
<span>
<button type="button" class="layui-btn layui-btn-primary upload-choose-btn" data-type="image" data-multiple="false">
<i class="fa fa-list"></i> 选择
</button>
</span>
</div>
<ul class="layui-row layui-col-xs12 layui-col-md9 preview-list layui-col-space5"></ul>
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label required">内容</label>
<div class="layui-input-block">
<textarea name="content" class="layui-textarea tinymce-editor">{$item.content ?? ''}</textarea>
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<input type="hidden" name="id" value="{$item.id ?? 0}">
<button class="layui-btn layui-btn-normal" data-url="{%path%}/edit" lay-submit lay-filter="saveBtn">确认保存</button>
</div>
</div>
</div>
</div>
</div>
<script src="__MANAGER__/{%jsPath%}?v={:mt_rand()}"></script>

68
app/command/stubs/index.stub Executable file
View File

@ -0,0 +1,68 @@
{layout name="manager/layout" /}
<div class="layui-row layui-col-space12">
<div class="layui-col-xs12 layui-col-md12">
<div class="layuimini-container location-index-page">
<div class="layuimini-main">
<fieldset class="table-search-fieldset" style="display: none">
<legend>搜索信息</legend>
<div style="margin: 10px 10px 10px 10px">
<form class="layui-form layui-form-pane" action="">
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label">下拉选择</label>
<div class="layui-input-block">
<select name="status">
<option value=""></option>
<option value="sss">SSS</option>
<option value="bbb">XXX</option>
</select>
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">关键词</label>
<div class="layui-input-block">
<input class="layui-input" name="keyword" placeholder="支持模糊查询">
</div>
</div>
<div class="layui-inline">
<button type="submit" class="layui-btn layui-btn-primary" lay-submit lay-filter="data-search-btn"><i class="layui-icon"></i> 搜 索</button>
</div>
</div>
</form>
</div>
</fieldset>
<div class="image-table">
<table id="table-container" class="layui-table" data-url="{%path%}/index" lay-filter="table-container-filter"></table>
</div>
</div>
</div>
</div>
</div>
<!-- 隐藏列 -->
<!-- 编辑单元格提交url -->
<input type="hidden" id="row-modify" data-url="{%path%}/modify">
<!-- 列-上下架 -->
<script type="text/html" id="row-saleable">
<input type="checkbox" name="saleable" value="{{d.id}}" lay-skin="switch" lay-text="是|否" lay-filter="changeSaleable" {{ d.saleable == 1 ? 'checked' : '' }}>
</script>
<!-- 操作列 -->
<script type="text/html" id="row-operate">
<a class="layui-btn layui-btn-primary layui-btn-xs" data-href="{%path%}/edit.html?id={{d.id}}" data-title="编辑" lay-event="edit">编辑</a>
<a class="layui-btn layui-btn-danger layui-btn-xs" data-href="{%path%}/del.html" lay-event="del">删除</a>
</script>
<!-- toolbar -->
<script type="text/html" id="toolbar-tpl">
<a class="layui-btn layui-btn-primary layui-btn-sm" data-table-refresh lay-event="refresh"><i class="fa fa-refresh"></i></a>
<a class="layui-btn layui-btn-normal layui-btn-sm" data-href="{%path%}/add.html" data-title="添加" lay-event="add">添加</a>
<a class="layui-btn layui-btn-danger layui-btn-sm" data-href="{%path%}/del.html" lay-event="del">删除</a>
</script>
<script src="__MANAGER__/{%jsPath%}?v={:mt_rand()}"></script>

211
app/command/stubs/js.stub Executable file
View File

@ -0,0 +1,211 @@
layui.use(['laytpl', 'table', 'jquery', 'form', 'miniTab', 'xmSelect', 'laydate'], function () {
let $ = layui.jquery,
table = layui.table,
xmSelect = layui.xmSelect,
miniTab = layui.miniTab,
laydate = layui.laydate,
form = layui.form;
/**** index begin ***/
if ($('.location-index-page').length > 0) {
miniTab.listen();
// 渲染表格
let listUrl = $('#table-container').data('url');
let insTb = table.render({
elem: '#table-container',
title: '列表',
defaultToolbar: ['filter', 'exports', {
title: '搜索' //自定义头部工具栏右侧图标。如无需自定义,去除该参数即可
, layEvent: 'search'
, icon: 'layui-icon-search'
}],
toolbar: '#toolbar-tpl',
method: 'POST',
url: listUrl,
page: true,
limit: 20,
limits: [20,50,100,200,500,1000],
request: {
pageName: 'page',
limitName: 'size',
},
parseData: function (res) {
return {
"code": res.code, //解析接口状态
"msg": res.msg, //解析提示文本
"count": res.data.total, //解析数据长度
"data": res.data.list //解析数据列表
};
},
cols: [[
{type: 'checkbox'},
{field: 'name', title: '名称', minWidth: 200},
{templet: '#row-saleable', minWidth: 120,align: 'center', title: '上下架'},
{field: 'sort', width: 150, align: 'center', title: '排序', edit: 'text'},
{field: 'published_at', title: '发布日期', minWidth: 150, hide: true},
{templet: '#row-operate', width: 280, align: 'center', title: '操作'}
]],
done: function () {
Tools.setInsTb(insTb);
}
});
//监听工具条 注意区别toolbar和tool toolbar是表头上的工具条 tool是行中的工具条
table.on('toolbar(table-container-filter)', function (obj) {
let layEvent = obj.event;
let insTb = Tools.getInsTb();
let url = $($(this).context).data('href')
let title = $($(this).context).data('title')
let width = $($(this).context).data('width') ? $($(this).context).data('width') : '100%';
let height = $($(this).context).data('height') ? $($(this).context).data('height') : '100%';
let checkStatus = table.checkStatus('table-container');
let selected = checkStatus.data;
let ids = [];
switch (layEvent) {
// toolbar 删除
case 'del':
if (checkStatus.data.length <= 0) {
layer.msg('请先选择数据');
return false;
}
// let selected = checkStatus.data;
// let ids = [];
$.each(selected, function (index, val) {
ids.push(val.id);
})
delRow(url, ids, insTb);
return false;
// toolbar 刷新
case 'refresh':
refreshTab(insTb);
return false;
// toolbar 搜索
case 'search':
let search = $('.table-search-fieldset');
if (search.hasClass('div-show')) {
search.css('display', 'none').removeClass('div-show');
} else {
search.css('display', 'block').addClass('div-show');
}
return false;
// 其他 默认为打开弹出层
default:
if (layEvent !== 'LAYTABLE_COLS' && layEvent !== 'LAYTABLE_EXPORT') {
openLayer(url, title, width, height);
return false;
}
}
});
//监听行工具条
table.on('tool(table-container-filter)', function (obj) {
let data = obj.data;
let layEvent = obj.event;
let url = $($(this).context).data('href');
let title = $($(this).context).data('title');
let width = $($(this).context).data('width') ? $($(this).context).data('width') : '100%';
let height = $($(this).context).data('height') ? $($(this).context).data('height') : '100%';
let insTb = Tools.getInsTb();
switch (layEvent) {
// 行 删除
case 'del':
let ids = [data.id];
delRow(url, ids, insTb);
return false;
//其他 默认为打开弹出层
default:
openLayer(url, title, width, height);
return false;
}
});
changeSwitch('changeSaleable');//监听上下架
let modifyUrl = $('#row-modify').data('url');
table.on('edit(table-container)', function (obj) {
let id = obj.data.id;
$.ajax(modifyUrl, {
data: {"id": id, "field": obj.field, "value": obj.value}
,dataType : 'json'
,type: 'POST'
})
.done(function (res) {
if (res.code === 0) {
insTb.reload();
}
})
});
// switch变更
function changeSwitch(filter) {
form.on('switch(' + filter + ')', function (obj) {
let val = obj.elem.checked ? 1 : 0;
$.post(modifyUrl, {id: this.value, field: this.name, value: val}, function (res) {
layer.msg(res.msg)
if (res.code !== 0) {
//操作不成功则刷新页面
insTb.reload();
}
})
});
}
// 监听搜索操作
form.on('submit(data-search-btn)', function (data) {
//执行搜索重载
table.reload('table-container', {
page: {curr: 1}
, where: data.field
}, 'data');
return false;
});
}
/*** index end ***/
if ($('.location-operate-page').length > 0) {
let parentCategory = $('#parent-category');
let categoryList = parentCategory.data('list') ? parentCategory.data('list') : [];
xmSelect.render({
el: '#parent-category',
paging: false,
autoRow: true,
clickClose: true,
name: 'category_id',
tips: '请选择分类',
direction: 'auto',
height: 'auto',
model: {
icon: 'hidden',
},
prop: {
name: 'title',
value: 'id',
},
tree: {
show: true,
strict: false,
clickCheck: true,
expandedKeys: true,
clickExpand: false
},
theme: {
color: '#1e84ff',
},
data: categoryList
});
laydate.render({
elem: '#published-at',
type: 'datetime',
});
}
});

739
app/common.php Executable file
View File

@ -0,0 +1,739 @@
<?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\facade\Config as CConfig;
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(array $data): array
{
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;
}
CConfig::load('extra/qiniu', 'qiniu');
$customDomain = config('qiniu')['customDomain'] ?? '';
$domain = $customDomain ?: $domain;
return $domain.$string;
}
}
if (!function_exists('checkPathExistWithMake')) {
/**
* 检测文件夹是否存在,不存在时自动创建(需要有写入权限)
* 支持递归创建
*
* @param string $absolutePath
* @return bool
*/
function checkPathExistWithMake(string $absolutePath): bool
{
try {
$absolutePath = rtrim($absolutePath, '/');
if (empty($absolutePath)) {
return false;
}
if (!is_dir($absolutePath)) {
return mkdir($absolutePath, 0777, true);
}
return true;
} catch (\Exception $e) {
return false;
}
}
}
if (!function_exists('replaceStoragePath'))
{
function replaceStoragePath ($content, $domain = ''){
if (empty($domain)) {
CConfig::load('extra/qiniu', 'qiniu');
$customDomain = config('qiniu')['customDomain'] ?? '';
$domain = $customDomain ?: request()->domain();
}
$pregRule = "/<[img|IMG].*?src=[\'|\"][\/storage]{1}(.*?(?:[\.jpg|\.jpeg|\.png|\.gif|\.bmp]))[\'|\"].*?[\/]?>/";
return preg_replace($pregRule, '< img src="' . $domain . '/${1}" style="max-width:100%">', (string)$content );
}
}
if (!function_exists('periodDate')) {
/**
* 获取期间日期所有日期
* @param $startDate
* @param $endDate
* @return array
*/
function periodDate($startDate, $endDate): array
{
$startTime = strtotime($startDate);
$endTime = strtotime($endDate);
$arr = array();
while ($startTime <= $endTime) {
$arr[] = date('Y-m-d', $startTime);
$startTime = strtotime('+1 day', $startTime);
}
return $arr;
}
}

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

@ -0,0 +1,42 @@
<?php
namespace app\controller;
use app\service\Redis;
use think\response\View;
/**
* 控制器基础类
*/
class Base extends BaseController
{
//需要向模板传递的值
protected $data = [];
//系统配置信息
protected $system = [];
protected $auth = [];
protected $authId = 0;
protected $redis = null;
// 初始化
protected function initialize()
{
$this->middleware = [
'login' => ['except' => $this->noNeedLogin],
];
$this->auth = session('frontend_auth') ?? [];
$this->data['auth'] = $this->auth;
$this->authId = $this->auth['id'] ?? 0;
$this->redis = Redis::instance();
}
//模板
protected function view($template = ''): View
{
return view($template)->assign($this->data);
}
}

254
app/controller/BaseController.php Executable file
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);
}
}

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

@ -0,0 +1,61 @@
<?php
namespace app\controller;
use app\service\Redis;
use think\response\View;
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);
}
public function notice(): View
{
if (Redis::instance()->get('activity_close') == 0) {
header('Location:/coupon/index');
exit;
}
return view();
}
}

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

@ -0,0 +1,102 @@
<?php
namespace app\controller;
use app\service\wx\WechatPay;
use Exception;
use think\facade\Log;
use think\response\Redirect;
use think\response\View;
class Index
{
/**
* @return \think\response\View
* @throws Exception
*/
public function index(): View
{
exit;
return view();
}
/**
* 退出
*
* @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);
}
}

116
app/controller/Login.php Executable file
View File

@ -0,0 +1,116 @@
<?php
namespace app\controller;
use app\model\Account;
use app\service\wx\Wechat;
use think\response\Redirect;
class Login extends Base
{
protected $noNeedLogin = ['index', 'temp'];
public function index(): Redirect
{
if (env('app_dev', false)) {
$this->temp();
exit;
}
$code = input('code/s', '');
$wechat = Wechat::getInstance();
if (empty($code)) {
$redirectUrl = $wechat->oauth->scopes(['snsapi_userinfo'])->redirect(request()->domain().'/login/index');
header('Location:'.$redirectUrl);
exit;
}
$user = $wechat->oauth->userFromCode($code);
if ($userInfo = $user->getRaw()) {
unset($userInfo['privilege']);
$field = 'id,openid,nickname,sex,headimgurl,unionid,business_id,phone_active,mobile';
if (!$account = Account::field($field)->where('openid', $userInfo['openid'])->find()) {
$userInfo['created_at'] = date('Y-m-d H:i:s');
$create = Account::create($userInfo);
$account = [
'id' => $create['id'],
'openid' => $create['openid'],
'nickname' => $create['nickname'],
'sex' => $create['sex'],
'headimgurl' => $create['headimgurl'],
'unionid' => $create['unionid'],
'business_id' => $create['business_id'],
'phone_active' => $create['phone_active'],
];
}
session('frontend_auth', $account);
}
return $this->redirect('/login/bind');
}
/**
* 本地登录 模拟微信code
*
* @return \think\response\Redirect
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function temp(): Redirect
{
$openid = 'o3LH9jktzObHsQOK-Uu83D4tr_Tg';//拙言号
$field = ['id', 'openid', 'nickname', 'sex', 'headimgurl', 'unionid', 'business_id', 'phone_active', 'mobile'];
$account = Account::field($field)->where('openid', $openid)->find();
if (!$account) {
$account = Account::findById(1, $field);
}
session('frontend_auth', $account->toArray());
return $this->redirect('/coupon/game');
}
/**
* 绑定手机
*
* @return \think\response\Redirect|\think\response\View|\think\response\Json
*/
public function bind()
{
if ($this->auth && $this->auth['phone_active'] > 0) {
if ($this->auth['business_id']) {
// 是商户负责人时 再次检验身份 避免用户身份变更但缓存未更新
// $businessId = Account::where('id', $this->authId)->value('business_id');
// if ($businessId > 0) {
// if ($businessId != $this->authId) {
// $this->auth['business_id'] = $businessId;
// session('frontend_auth', $this->auth);
// }
// }
return $this->redirect('/business/my');
}
return $this->redirect('/coupon/index');
}
if ($this->request->isPost()) {
$phone = input('phone/s', '');
if (!checkMobile($phone)) {
return $this->json(4000, '请填写正确的手机号');
}
$this->auth['mobile'] = $phone;
$this->auth['phone_active'] = 1;
session('frontend_auth', $this->auth);
Account::where('id', $this->authId)->save(['phone_active' => 1, 'mobile' => $phone]);
$url = $this->auth['business_id'] ? '/business/my' : '/coupon/index';
return $this->json(0, 'success', ['url' => $url]);
}
return $this->view();
}
}

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

@ -0,0 +1,158 @@
<?php
namespace app\controller;
use app\model\File;
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 = false;
private $validate = null;
// 文件上传对外默认保存目录(相对路径)
private string $uploadPath = '';
private $videoUploadPath;
// 文件上传对外默认保存目录是否有写权限
private bool $uploadPathIsWritable = false;
private $videoUploadPathIsWritable = 0;
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');
$this->videoUploadPath = Config::get('filesystem.disks.video.url');
if (!is_writable(app()->getRootPath().'public'.$this->uploadPath)) {
mkdir(app()->getRootPath().'public'.$this->uploadPath, 0777, true);
}
$this->uploadPathIsWritable = 1;
if (!is_writable(app()->getRootPath().'public'.$this->videoUploadPath)) {
mkdir(app()->getRootPath().'public'.$this->videoUploadPath, 0777, true);
}
$this->videoUploadPathIsWritable = 1;
}
/**
* 通用文件上传
* @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, '请上传图片文件');
}
if (!in_array($image->getMime(), ['image/png', 'image/jpeg', 'image/bmp'])) {
return $this->json(4001, '请上传png|jpeg|jpg|bmp格式图片');
}
if ($image->getSize() > 1.5 * 1024 * 1024) {
return $this->json(4001, '图片超出尺寸要求');
}
try {
if (!$this->uploadPathIsWritable) {
throw new \Exception('上传文件夹需要写入权限');
}
$src = Filesystem::putFile('images/'.date('Ym'), $image, 'uniqid');
$src = $this->uploadPath.'/'.$src;
$return['src'] = $src;
} catch (\Exception $e) {
return $this->json(4003, $e->getMessage());
}
return $this->json(0, 'success', $return);
}
/**
* 通用视频上传
* @return Json
*/
public function video()
{
try {
$file = request()->file('video');
$md5 = $file->md5();//文件md5
if ($this->validate->checkVideo($file)) {
if (!$this->videoUploadPathIsWritable) {
throw new \Exception('上传文件夹需要写入权限');
}
$src = Filesystem::putFile('videos/'.date('Ym'), $file, 'uniqid');
$src = $this->uploadPath.'/'.$src;
$return['src'] = $src;
$return['full_src'] = resourceJoin($src);
//加入上传文件表
File::add($file, $src, $md5, 'video');
return $this->json(0, 'success', $return);
} else {
$errorMsg = Lang::get($this->validate->getError());
return $this->json(4002, $errorMsg);
}
} catch (\Exception $e) {
return $this->json(4000, $e->getMessage());
}
}
}

30
app/controller/api/Base.php Executable file
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,244 @@
<?php
namespace app\controller\api\v1;
use app\controller\api\Base;
use app\model\Account;
use app\model\CheckLog;
use app\model\OvertimeLog;
use app\model\Position;
use app\model\Worksite;
use app\repository\CommonRepository;
use app\repository\OperationRepository;
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',
'getCurrentWorksite',
'worksiteList',
'positionList',
'notice',
];
/**
* 发送短信验证码
*
* @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);
$domain = $this->request->domain();
$list->each(function ($item) use ($domain) {
$item->src = resourceJoin($item->src, $domain);
unset($item->created_at);
unset($item->sort);
});
} catch (\Exception $e) {
$list = new Collection();
}
return $this->json(0, 'success', $list);
}
// 根据经纬度获取所在工地 500米
public function getCurrentWorksite(): Json
{
$lng = input('lng/s');
$lat = input('lat/s');
if (empty($lng) || empty($lat)) {
return $this->json(4002, "参数错误");
}
return $this->json(0, 'success', Worksite::getNearest($lng, $lat, 200));
}
// 工地列表 todo 如果有相关用户 则获取该用户相关的工地列表 如工人 则只获取其参与过的工地列表 需要添加一个字段来确认是否根据用户过滤
public function worksiteList(): Json
{
$page = input('page/d', 1);
$size = input('size/d', 500);
$keyword = input('keyword/s');
// $accountId = $this->request->user['user_id'] ?? 0;
$where = [];
if (!empty($keyword)) {
$where[] = ['name|address', 'like', '%'.$keyword.'%'];
}
$where[] = ['status', '=', Worksite::COMMON_ON];
$query = Worksite::where($where);
$total = $query->count();
$res = [
'total' => $total,
'current' => $page ?: 1,
'size' => $size ?: 500,
'list' => new Collection(),
];
if ($total > 0) {
$res['list'] = $query->page($page, $size)
->field('id,name')
->order('sort', 'desc')
->order('id', 'desc')
->select();
}
return $this->json(0, 'success', $res);
}
// 技术岗位列表
public function positionList(): Json
{
$page = input('page/d', 1);
$size = input('size/d', 500);
$keyword = input('keyword/s');
$where = [];
if (!empty($keyword)) {
$where[] = ['name', 'like', '%'.$keyword.'%'];
}
$query = Position::where($where);
$total = $query->count();
$res = [
'total' => $total,
'current' => $page ?: 1,
'size' => $size ?: 500,
'list' => new Collection(),
];
if ($total > 0) {
$res['list'] = $query->page($page, $size)
->field('id,name')
->order('sort', 'desc')
->order('id', 'desc')
->select();
}
return $this->json(0, 'success', $res);
}
/**
* 加班申请详情
*
* @return \think\response\Json
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function overtimeInfo(): Json
{
$accountId = $this->request->user['user_id'] ?? 0;
$id = input('id/d');
if (!$account = Account::where('id', $accountId)->field('id,role')->find()) {
return $this->json(6001, '请先登录'.$accountId, $account);
}
if (!$item = OvertimeLog::find($id)) {
return $this->json(4004, '加班记录不存在');
}
if ($item['account_id'] != $accountId && $account['role'] != Account::ROLE_MANAGER) {
return $this->json(4003, '无权查看');
}
return $this->json(0,'success', $item);
}
/**
* 获取通知消息 在打卡页面每次加载都需要请求此接口,查看是否需要展示信息
* 如果有登录token就查询用户相关小题
* 如果没有登录token则返回空
*
* @return \think\response\Json
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function notice(): Json
{
$accountId = $this->request->user['user_id'] ?? 0;
if ($accountId == 0) {
return $this->json(0, 'success', ['notice' => 0, 'msg' => '', 'id' => 0]);
}
if (!$account = Account::find($accountId)) {
return $this->json(0, 'success', ['notice' => 0, 'msg' => '', 'id' => 0]);
}
if ($account['role'] != Account::ROLE_NORMAL) {
return $this->json(0, 'success', ['notice' => 0, 'msg' => '', 'id' => 0]);
}
$checkLog = CheckLog::where('account_id', $accountId)->where('is_register', Account::COMMON_ON)->order('id', 'desc')->find();
if (empty($checkLog)) {
return $this->json(0, 'success', ['notice' => 0, 'msg' => '', 'id' => 0]);
}
if ($checkLog['status'] != -1) {
return $this->json(0, 'success', ['notice' => 0, 'msg' => '', 'id' => 0]);
}
return $this->json(0, 'success', ['notice' => 1, 'msg' => $checkLog['refund_reason'], 'id' => $checkLog['id']]);
}
}

View File

@ -0,0 +1,394 @@
<?php
namespace app\controller\api\v1;
use app\controller\api\Base;
use app\model\Account;
use app\model\GoodsArea;
use app\model\GoodsCategory;
use think\Collection;
use think\facade\Log;
use think\response\Json;
class Goods extends Base
{
protected $noNeedLogin = [
'list', 'detail', 'category', 'area'
];
// 物品列表
public function list(): Json
{
$page = input('page/d', 1);
$size = input('size/d', 20);
$keyword = input('keyword/s');
$categoryId = input('category_id/d', 0);
$areaId = input('area_id/d', 0);
$where = [];
if (!empty($keyword)) {
$where[] = ['a.nickname|g.title|gc.title|g.phone|ga.title', 'like', '%'.$keyword.'%'];
}
if ($categoryId > 0) {
$where[] = ['g.category_id', '=', $categoryId];
}
if ($areaId > 0) {
$where[] = ['g.area_id', '=', $areaId];
}
$where[] = ['g.status', '=', 0];//进行中
$query = \app\model\Goods::alias('g')
->leftJoin('goods_category gc', 'gc.id = g.category_id')
->leftJoin('account a', 'a.id = g.account_id')
->leftJoin('goods_area ga', 'ga.id = g.area_id')
->field('g.id,g.cover,g.title,g.images,price,original_price')
->where($where);
$total = $query->count();
$res = [
'total' => $total,
'current' => $page ?: 1,
'size' => $size ?: 20,
'list' => new Collection(),
];
if ($total > 0) {
$res['list'] = $query->page($page, $size)->order('g.sort', 'desc')->order('g.id', 'desc')->select();
$domain = request()->domain(true);
$res['list']->each(function ($item) use ($domain) {
$item->cover = resourceJoin((string) $item->cover, $domain);
$images = explode(',', $item->images);
foreach ($images as $k => $img) {
if (!empty($img)) {
$images[$k] = resourceJoin($img, $domain);
} else {
unset($images[$k]);
}
}
$item->images = $images;
});
}
return $this->json(0, 'success', $res);
}
// 物品详情
public function detail(): Json
{
$id = input('id/d', 0);
$field = 'g.id,g.title,g.cover,g.images,g.content,g.price,g.original_price,g.phone,ga.title as area_name,gc.title as category_name';
$item = \app\model\Goods::alias('g')
->leftJoin('goods_area ga','ga.id = g.area_id')
->leftJoin('goods_category gc','gc.id = g.category_id')
->where('g.status', 0)
->where('g.id', $id)
->field($field)
->find();
if (!$item) {
return $this->json(4004, '物品不存在或已售出');
}
$domain = request()->domain(true);
$item->cover = resourceJoin((string) $item->cover, $domain);
$images = explode(',', $item->images);
foreach ($images as &$img) {
$img = resourceJoin($img, $domain);
}
$item->images = $images;
return $this->json(0, 'success', $item);
}
/**
* 物品添加
* @return \think\response\Json
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @throws \Exception
*/
public function add(): Json
{
if (!$this->request->isPost()) {
return $this->json(4000, '无效请求');
}
$accountId = $this->request->user['user_id'] ?? 0;
if (!$account = Account::field('id,nickname,status')->find((int) $accountId)) {
return $this->json(6001, '请先登录');
}
if ($account['status'] != Account::STATUS_NORMAL) {
return $this->json(4003, '用户状态异常');
}
$input = input('post.');
$rules = [
'title|标题' => 'require|max:100|min:4',
'cover|封面图' => 'require',
'category_id|所属分类' => 'require',
'area_id|所属小区' => 'require',
'price|价格' => 'require|number',
];
$validate = $this->validateByApi($input, $rules);
if ($validate !== true) {
return $validate;
}
if (!isset($input['images']) && !isset($input['content'])) {
return $this->json(4005, '详情组图和内容至少填一个');
}
try {
$originalPrice = 0;
if (isset($input['original_price'])) {
$originalPrice = $input['original_price'] ?: $input['price'];
}
\app\model\Goods::create([
'title' => $input['title'],
'cover' => $input['cover'],
'category_id' => $input['category_id'],
'content' => $input['content'] ?? '',
'phone' => $input['phone'] ?? '',
'price' => $input['price'] ?? 0,
'original_price' => $originalPrice,
'account_id' => $accountId,
'area_id' => $input['area_id'] ?? 0,
'images' => $input['images'] ?? '',
'created_at' => date('Y-m-d H:i:s'),
]);
return $this->json();
} catch (\Exception $e) {
Log::error('闲置物品添加失败line '.$e->getLine().' '.$e->getMessage());
return $this->json(5000, '物品添加失败');
}
}
/**
* 物品编辑
*
* @return \think\response\Json
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @throws \Exception
*/
public function edit(): Json
{
if (!$this->request->isPost()) {
return $this->json(4000, '无效请求');
}
$accountId = $this->request->user['user_id'] ?? 0;
if (!$account = Account::field('id,nickname,status')->find((int) $accountId)) {
return $this->json(6001, '请先登录');
}
if ($account['status'] != Account::STATUS_NORMAL) {
return $this->json(4003, '用户状态异常');
}
$input = input('post.');
$rules = [
'id|ID' => 'require|number',
'title|标题' => 'require|max:100|min:4',
'cover|封面图' => 'require',
'category_id|所属分类' => 'require',
'area_id|所属小区' => 'require',
'price|价格' => 'require|number',
];
$validate = $this->validateByApi($input, $rules);
if ($validate !== true) {
return $validate;
}
try {
if (!$item = \app\model\Goods::find((int) $input['id'])) {
return $this->json(4004, '记录不存在');
}
if ($item['account_id'] != $accountId) {
return $this->json(4003, '不是您发布的物品');
}
if (!isset($input['images']) && !isset($input['content'])) {
return $this->json(4005, '详情组图和内容至少填一个');
}
$originalPrice = 0;
if (isset($input['original_price'])) {
$originalPrice = $input['original_price'] ?: $input['price'];
}
$item->save([
'title' => $input['title'],
'cover' => $input['cover'],
'category_id' => $input['category_id'],
'content' => $input['content'] ?? '',
'phone' => $input['phone'] ?? '',
'price' => $input['price'] ?? 0,
'original_price' => $originalPrice,
'area_id' => $input['area_id'] ?? 0,
'images' => $input['images'] ?? '',
]);
return $this->json();
} catch (\Exception $e) {
Log::error('闲置物品编辑失败line '.$e->getLine().' '.$e->getMessage());
return $this->json(5000, '物品编辑失败');
}
}
/**
* 删除
*
* @return \think\response\Json
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function del(): Json
{
if (!$this->request->isPost()) {
return $this->json(4000, '无效请求');
}
$accountId = $this->request->user['user_id'] ?? 0;
if (!$account = Account::field('id,nickname,status')->find((int) $accountId)) {
return $this->json(6001, '请先登录');
}
if ($account['status'] != Account::STATUS_NORMAL) {
return $this->json(4003, '用户状态异常');
}
$ids = input('id');
try {
$ids = explode(',', $ids);
if (!empty($ids)) {
\app\model\Goods::whereIn('id', $ids)->where('account_id', $accountId)->delete();
}
return $this->json();
} catch (\Exception $e) {
Log::error('闲置物品删除失败line '.$e->getLine().' '.$e->getMessage());
return $this->json(5000, '闲置物品删除失败');
}
}
// 小区
public function area(): Json
{
$page = input('page/d', 1);
$size = input('size/d', 20);
$keyword = input('keyword/s');
$where = [];
if (!empty($keyword)) {
$where[] = ['title', 'like', '%'.$keyword.'%'];
}
$query = GoodsArea::field('id,title')->where($where);
$total = $query->count();
$res = [
'total' => $total,
'current' => $page ?: 1,
'size' => $size ?: 20,
'list' => new Collection(),
];
if ($total > 0) {
$res['list'] = $query->page($page, $size)->order('sort', 'desc')->order('id', 'desc')->select();
}
return $this->json(0, 'success', $res);
}
// 分类
public function category(): Json
{
$keyword = input('keyword/s');
$where = [];
if (!empty($keyword)) {
$where[] = ['title', 'like', '%'.$keyword.'%'];
}
$res = GoodsCategory::field('id,title')->where($where)->order('sort', 'desc')->order('id', 'asc')->select();
return $this->json(0, 'success', $res);
}
/**
* 设置状态 0=展示 1=不展示(已完成)
*
* @return \think\response\Json
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function setStatus(): Json
{
if (!$this->request->isPost()) {
return $this->json(4000, '无效请求');
}
$accountId = $this->request->user['user_id'] ?? 0;
if (!$account = Account::field('id,nickname,status')->find((int) $accountId)) {
return $this->json(6001, '请先登录');
}
if ($account['status'] != Account::STATUS_NORMAL) {
return $this->json(4003, '用户状态异常');
}
$id = input('id/d');
$status = input('status/d', 0);
if (!in_array($status, [0, 1])) {
return $this->json(4001, '参数错误');
}
try {
if (!$item = \app\model\Goods::findById($id, ['id', 'status', 'account_id'])) {
return $this->json(4004, '物品不存在');
}
if ($item['account_id'] != $accountId) {
return $this->json(4003, '不是您发布的物品');
}
$item->save([
'status' => $status
]);
return $this->json();
} catch (\Exception $e) {
Log::error('闲置物品状态修改失败line '.$e->getLine().' '.$e->getMessage());
return $this->json(5000, '状态设置失败');
}
}
}

View File

@ -0,0 +1,178 @@
<?php
namespace app\controller\api\v1;
use app\controller\api\Base;
use app\exception\RepositoryException;
use app\repository\AccountRepository;
use app\service\ExtraConfig;
use app\service\order\Compute;
use Exception;
use think\facade\Config;
use think\facade\Config as CConfig;
use think\response\Json;
class Index extends Base
{
protected $noNeedLogin = [
'baseConfig',
'statement',
'agreement',
'about',
'notice',
'safeNotice',
'handbook',
'computeDay',
'computeMonth',
];
public function index(): Json
{
return json(['code' => 0, 'msg' => 'I am index']);
}
/**
* 小程序个性装修配置
*/
public function miniProgramSetting(): Json
{
$conf = ExtraConfig::miniProgram();
return $this->json(0, 'success', $conf);
}
/**
* 基础配置
*
* @return Json
*/
public function baseConfig(): Json
{
try {
Config::load('extra/base', 'base');
$baseConfig = config('base');
$baseConfig['show_video'] = $baseConfig['show_video'] ?? 0;
$baseConfig['hide_video_cover'] = $baseConfig['hide_video_cover'] ?? '';
$res = [
'video_upload_max' => 50,//视频最大上传50M
'hide_video_cover' => resourceJoin($baseConfig['hide_video_cover']),//视频隐藏后 用来替换的图片
];
return $this->json(0, 'success', $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' => replaceStoragePath($content)]);
} catch (Exception $e) {
return $this->json(5000, '获取免责声明失败');
}
}
/**
* 用户协议
*
* @return Json
*/
public function agreement(): Json
{
try {
CConfig::load('extra/agreement', 'agreement');
$content = config('agreement')['content'] ?? '';
return $this->json(0, 'success', ['content' => replaceStoragePath($content)]);
} catch (Exception $e) {
return $this->json(5000, '获取用户协议失败');
}
}
/**
* 关于我们
*
* @return Json
*/
public function about(): Json
{
try {
CConfig::load('extra/about', 'about');
$res = config('about') ?? [];
$res['content'] = replaceStoragePath($res['content']);
return $this->json(0, 'success', $res);
} catch (Exception $e) {
return $this->json(5000, '获取关于我们失败');
}
}
/**
* 安全公告
*
* @return Json
*/
public function safeNotice(): Json
{
try {
CConfig::load('extra/safeNotice', 'safeNotice');
$res = config('safeNotice') ?? [];
$res['content'] = replaceStoragePath($res['content'] ?? '');
return $this->json(0, 'success', $res);
} catch (Exception $e) {
return $this->json(5000, '获取安全公告失败'.$e->getMessage());
}
}
/**
* 公告
*
* @return Json
*/
public function notice(): Json
{
try {
CConfig::load('extra/notice', 'notice');
$res = config('notice') ?? [];
$res['content'] = replaceStoragePath($res['content'] ?? '');
return $this->json(0, 'success', $res);
} catch (Exception $e) {
return $this->json(5000, '获取公告失败'.$e->getMessage());
}
}
/**
* 员工手册
*
* @return Json
*/
public function handbook(): Json
{
try {
CConfig::load('extra/handbook', 'handbook');
$res = config('handbook') ?? [];
$res['content'] = replaceStoragePath($res['content'] ?? '');
return $this->json(0, 'success', $res);
} catch (Exception $e) {
return $this->json(5000, '获取员工手册失败'.$e->getMessage());
}
}
// 定时任务 每月指定时间统计日工资 需要在月工资统计前执行
public function computeDay(): Json
{
\app\service\Pay::statistic();
return $this->json(0,'success', '统计日工资');
}
// 定时任务 每月指定时间统计月工资 需要在月工资统计前执行
public function computeMonth(): Json
{
\app\service\Pay::generateMonthLog();
return $this->json(0,'success', '统计月工资');
}
}

View File

@ -0,0 +1,637 @@
<?php
namespace app\controller\api\v1;
use app\controller\api\Base;
use app\model\Account;
use app\model\AccountWorksite;
use app\model\CheckLog;
use app\model\ClockLog;
use app\model\OvertimeLog;
use app\model\Position;
use app\model\Worksite;
use app\service\Math;
use Exception;
use Lcobucci\Clock\Clock;
use think\Collection;
use think\response\Json;
/**
* 负责人相关
*
* Class Manager
* @package app\controller\api\v1
*/
class Manager extends Base
{
protected $noNeedLogin = [];
/**
* 查看审核详情
*
* @return Json
*/
public function checkDetail(): Json
{
try {
$accountId = $this->request->user['user_id'] ?? 0;
$id = input('id/d');//待审核记录ID
if (!$account = Account::findById($accountId, ['id, role'])) {
return $this->json(6001, '请先登录');
}
if ($account['role'] <= Account::COMMON_ON) {
// 工地负责人才能查看
return $this->json(4003, '无查看权限');
}
if (!$item = CheckLog::findById($id)) {
return $this->json(4004, '记录不存在');
}
$item = arrayNullToString($item->toArray());
$fields = Account::needCheckFields();
array_push($fields, 'id', 'role', 'work_at', 'worksite_id');
$user = Account::findById($item['account_id'], $fields)->toArray();
$user = arrayNullToString($user);
$positionList = Position::column('name', 'id');
$item['position_name'] = $positionList[$item['position']] ?? '';
$user['position_name'] = $positionList[$user['position']] ?? '';
// user角色为工人时需要判断当前人是否是该工地的负责人
if (!empty($user) && $user['role'] == Account::COMMON_ON) {
$managerIds = AccountWorksite::where('worksite_id', $user['worksite_id'])->column('account_id');
if (!in_array($accountId, $managerIds)) {
return $this->json(4003, '无查看权限2');
}
}
$item['account'] = $user;
return $this->json(0, 'success', $item);
} catch (Exception $e) {
return $this->json(4000, '没有相关的用户记录'.$e->getMessage());
}
}
// 审核列表(员工资料提交的审核)
public function checkList(): Json
{
$page = input('page/d', 1);
$size = input('size/d', 20);
$keyword = input('keyword/s');
$status = input('status/d', 0);//状态 0=待审核 1=已审核包含1通过 -1不通过
$accountId = $this->request->user['user_id'] ?? 0;
if (!$account = Account::findById($accountId, ['id, role'])) {
return $this->json(6001, '请先登录');
}
if ($account['role'] <= Account::COMMON_ON) {
return $this->json(4003, '无查看权限');
}
$where = [];
if (!empty($keyword)) {
$where[] = ['cl.real_name|cl.mobile', 'like', '%'.$keyword.'%'];
}
if ($status == 0) {
$where[] = ['cl.status', '=', 0];
} else {
$where[] = ['cl.status', 'in', [1, -1]];
}
// 负责工地
$worksiteIds = AccountWorksite::where('account_id', $accountId)->column('worksite_id');
$where[] = ['cl.worksite_id', 'in', $worksiteIds];
$query = CheckLog::alias('cl')
->leftJoin('account a', 'a.id = cl.account_id')
->leftJoin('position p', 'p.id = cl.position')
->field('cl.id,cl.real_name,cl.status,cl.created_at,p.name as position_name')
->where($where);
$total = $query->count();
$res = [
'total' => $total,
'current' => $page ?: 1,
'size' => $size ?: 20,
'list' => new Collection(),
];
if ($total > 0) {
$res['list'] = $query->page($page, $size)->order('cl.id', 'desc')->select();
$res['list']->each(function ($item) {
$item->created_at = date('Y年m月d日 H:i:s', strtotime($item->created_at));
});
$res['list'] = arrayNullToString($res['list']->toArray());
}
return $this->json(0, 'success', $res);
}
/**
* 审核资料(工人信息注册或二次修改审核)
*
* @return Json
*/
public function checkInfo(): Json
{
try {
$accountId = $this->request->user['user_id'] ?? 0;
$type = input('type/d', 1);//类型 1=通过 0=不通过
$id = input('id/d');//待审核记录ID
$reason = input('reason/s');//不通过原因
if (!in_array($type, [0, 1])) {
return $this->json(4001, '审核参数错误');
}
if (!$account = Account::findById($accountId, ['id, role'])) {
return $this->json(6001, '请先登录');
}
if ($account['role'] <= Account::COMMON_ON) {
// 工地负责人才能操作
return $this->json(4003, '无此权限');
}
if (!$item = CheckLog::findById($id)) {
return $this->json(4004, '记录不存在');
}
if ($item['status'] != CheckLog::COMMON_OFF) {
return $this->json(4002, '状态错误');
}
$user = Account::getUser($item['account_id']);
// user角色为工人时需要判断当前人是否是该工地的负责人
if (!empty($user) && $user['role'] == Account::COMMON_ON) {
$managerIds = AccountWorksite::where('worksite_id', $user['worksite_id'])->column('account_id');
if (!in_array($accountId, $managerIds)) {
return $this->json(4003, '无此权限2');
}
}
// 审核不通过
if ($type == 0) {
Account::where('id', $item['account_id'])->save(['checking' => Account::COMMON_OFF]);
$item->save(['status' => -1, 'refund_reason' => $reason]);
return $this->json();
}
$now = date('Y-m-d H:i:s');
$update = $item->toArray();
if ($update['is_register'] == CheckLog::COMMON_ON) {
$update['role'] = CheckLog::COMMON_ON;
$update['work_at'] = $now;
}
$update['checking'] = Account::COMMON_OFF;
unset($update['id']);
unset($update['account_id']);
unset($update['is_register']);
unset($update['status']);
unset($update['created_at']);
unset($update['check_at']);
unset($update['refund_reason']);
Account::where('id', $item['account_id'])->save($update);
$item->save(['status' => 1, 'check_at' => $now]);
return $this->json();
} catch (Exception $e) {
return $this->json(4000, '没有相关的用户记录'.$e->getMessage());
}
}
// 打卡记录
public function clockList(): Json
{
$page = input('page/d', 1);
$size = input('size/d', 20);
$status = input('status/d', 0);//状态 0=待审核 1=已审核包含1通过 -1不通过
$accountId = $this->request->user['user_id'] ?? 0;
if (!$account = Account::findById($accountId, ['id, role'])) {
return $this->json(6001, '请先登录');
}
if ($account['role'] <= Account::COMMON_ON) {
return $this->json(4003, '无权限');
}
$where = [];
if ($status == 0) {
$where[] = ['cl.status', '=', 0];
} else {
$where[] = ['cl.status', 'in', [1, -1]];
}
// 负责工地
$worksiteIds = AccountWorksite::where('account_id', $accountId)->column('worksite_id');
$where[] = ['cl.worksite_id', 'in', $worksiteIds];
$query = \app\model\ClockLog::alias('cl')
->leftJoin('account a', 'a.id = cl.account_id')
->leftJoin('worksite w', 'w.id = cl.worksite_id')
->field('cl.*,w.name as worksite_name,a.real_name as worker_name')
->where($where);
$total = $query->count();
$res = [
'total' => $total,
'current' => $page ?: 1,
'size' => $size ?: 20,
'list' => new Collection(),
];
if ($total > 0) {
$res['list'] = $query->page($page, $size)->order('cl.id', 'desc')->select();
$res['list']->each(function ($item) {
$item->type_text = $item->type == 'in' ? '上班' : '下班';
switch ($item->status) {
case 0:
$item->status_text = '待确认';
break;
case 1:
$item->status_text = '已确认';
break;
case -1:
$item->status_text = '不通过';
break;
}
unset($item->check_by);
unset($item->check_at);
unset($item->account_id);
unset($item->create_time);
unset($item->type);
unset($item->worksite_id);
});
$res['list'] = arrayNullToString($res['list']->toArray());
}
return $this->json(0, 'success', $res);
}
/**
* 审核打卡 支持批量
*
* @return Json
*/
public function checkClock(): Json
{
try {
$accountId = $this->request->user['user_id'] ?? 0;
$type = input('type/d', 1);//类型 1=通过 0=不通过
$ids = input('id/s');//待审核记录ID 多个用逗号分割
$ids = explode(',', $ids);
$ids = array_filter($ids);
if (!in_array($type, [0, 1])) {
return $this->json(4001, '审核参数错误');
}
if (!$account = Account::findById($accountId, ['id, role'])) {
return $this->json(6001, '请先登录');
}
if ($account['role'] <= Account::COMMON_ON) {
// 工地负责人才能操作
return $this->json(4003, '无此权限');
}
$worksiteIds = AccountWorksite::where('account_id', $accountId)->column('worksite_id');
if (ClockLog::whereIn('id', $ids)->whereNotIn('worksite_id', $worksiteIds)->count() > 0) {
return $this->json(4003, '部分记录不在您权限操作的范围');
}
ClockLog::whereIn('id', $ids)->where('status', ClockLog::COMMON_OFF)->update([
'status' => $type == 1 ? 1 : -1,
'check_at' => date('Y-m-d H:i:s'),
'check_by' => $accountId
]);
return $this->json();
} catch (Exception $e) {
return $this->json(5000, '审核失败'.$e->getMessage());
}
}
// 加班记录
public function overtimeList(): Json
{
$page = input('page/d', 1);
$size = input('size/d', 20);
$status = input('status/d', 0);//状态 0=待审核 1=已审核包含1通过 -1不通过
$accountId = $this->request->user['user_id'] ?? 0;
if (!$account = Account::findById($accountId, ['id, role'])) {
return $this->json(6001, '请先登录');
}
if ($account['role'] <= Account::COMMON_ON) {
return $this->json(4003, '无权限');
}
$where = [];
if ($status == 0) {
$where[] = ['cl.status', '=', 0];
} else {
$where[] = ['cl.status', 'in', [1, -1]];
}
// 负责工地
$worksiteIds = AccountWorksite::where('account_id', $accountId)->column('worksite_id');
$where[] = ['cl.worksite_id', 'in', $worksiteIds];
$query = \app\model\OvertimeLog::alias('cl')
->leftJoin('account a', 'a.id = cl.account_id')
->leftJoin('worksite w', 'w.id = cl.worksite_id')
->field('cl.*,w.name as worksite_name,a.real_name as worker_name')
->where($where);
$total = $query->count();
$res = [
'total' => $total,
'current' => $page ?: 1,
'size' => $size ?: 20,
'list' => new Collection(),
];
if ($total > 0) {
$res['list'] = $query->page($page, $size)->order('cl.id', 'desc')->select();
$res['list']->each(function ($item) {
switch ($item->status) {
case 0:
$item->status_text = '待确认';
break;
case 1:
$item->status_text = '已确认';
break;
case -1:
$item->status_text = '不通过';
break;
}
$item->create_time = date('Y年m月d日 H:i:s', $item->create_time);
$item->worktime = date('Y年m月d日', strtotime($item->day));
unset($item->check_by);
unset($item->check_at);
unset($item->account_id);
unset($item->created_at);
unset($item->type);
unset($item->day);
unset($item->day_text);
unset($item->worksite_id);
unset($item->is_statistic
);
});
$res['list'] = arrayNullToString($res['list']->toArray());
}
return $this->json(0, 'success', $res);
}
/**
* 审核加班 支持批量
*
* @return Json
*/
public function checkOvertime(): Json
{
try {
$accountId = $this->request->user['user_id'] ?? 0;
$type = input('type/d', 1);//类型 1=通过 0=不通过
$ids = input('id/s');//待审核记录ID 多个用逗号分割
$ids = explode(',', $ids);
$ids = array_filter($ids);
if (!in_array($type, [0, 1])) {
return $this->json(4001, '审核参数错误');
}
if (!$account = Account::findById($accountId, ['id, role'])) {
return $this->json(6001, '请先登录');
}
if ($account['role'] <= Account::COMMON_ON) {
// 工地负责人才能操作
return $this->json(4003, '无此权限');
}
$worksiteIds = AccountWorksite::where('account_id', $accountId)->column('worksite_id');
if (OvertimeLog::whereIn('id', $ids)->whereNotIn('worksite_id', $worksiteIds)->count() > 0) {
return $this->json(4003, '部分记录不在您权限操作的范围');
}
OvertimeLog::whereIn('id', $ids)->where('status', ClockLog::COMMON_OFF)->update([
'status' => $type == 1 ? 1 : -1,
'check_at' => date('Y-m-d H:i:s'),
'check_by' => $accountId
]);
return $this->json();
} catch (Exception $e) {
return $this->json(5000, '审核失败'.$e->getMessage());
}
}
// 工资记录
public function payListMock(): Json
{
$page = input('page/d', 1);
$size = input('size/d', 20);
$status = input('status/d', -1);
$date = input('data/s', '');
$accountId = $this->request->user['user_id'] ?? 0;
$where = [];
if (!empty($keyword)) {
$where[] = ['w.name', 'like', '%'.$keyword.'%'];
}
if ($status >= 0) {
$where[] = ['w.status', '=', $status];
}
if (!empty($date)) {
$date = str_replace('_', '', $date);
$where[] = ['cl.time', '=', $date];
}
$where[] = ['cl.account_id', '=', $accountId];
// $query = \app\model\ClockLog::alias('cl')
// ->leftJoin('worksite w', 'w.id = cl.worksite_id')
// ->field('cl.*,w.name as worksite_name')
// ->where($where);
//
// $total = $query->count();
$total = 20;
$res = [
'total' => $total,
'current' => $page ?: 1,
'size' => $size ?: 20,
'info' => [],
'list' => new Collection(),
];
if ($total > 0) {
$res['info'] = [
'amount' => 999999,//工资总金额
'base_amount' => 20000,//基本工资
'overtime_amount' => 20000,//加班工资
'not_amount' => 20000,//未发放
'done_amount' => 20000,//已发放
];
$res['list'] = [
[
'name' => '张珊珊',
'status' => 0,
'status_text' => '待发放',
'base_amount' => 7500,
'overtime_amount' => 0,
'amount' => 7500,
],
[
'name' => '李珊珊',
'status' => 0,
'status_text' => '待发放',
'base_amount' => 7500,
'overtime_amount' => 0,
'amount' => 7500,
],
[
'name' => '王珊珊',
'status' => 0,
'status_text' => '待发放',
'base_amount' => 7500,
'overtime_amount' => 0,
'amount' => 7500,
],
[
'name' => '张珊珊',
'status' => 0,
'status_text' => '待发放',
'base_amount' => 7500,
'overtime_amount' => 0,
'amount' => 7500,
],
[
'name' => '刘珊珊',
'status' => 0,
'status_text' => '待发放',
'base_amount' => 7500,
'overtime_amount' => 0,
'amount' => 7500,
],
[
'name' => '谢珊珊',
'status' => 0,
'status_text' => '待发放',
'base_amount' => 7500,
'overtime_amount' => 0,
'amount' => 7500,
],
[
'name' => '吴珊珊',
'status' => 0,
'status_text' => '待发放',
'base_amount' => 7500,
'overtime_amount' => 0,
'amount' => 7500,
],
];
}
return $this->json(0, 'success', $res);
}
// 工资记录
public function payList(): Json
{
$page = input('page/d', 1);
$size = input('size/d', 20);
$status = input('status/d', -1);
$date = input('date/s', '');
$accountId = $this->request->user['user_id'] ?? 0;
$where = [];
if (!empty($date)) {
$date = str_replace('_', '', $date);
$where[] = ['pml.time', '=', $date];
}
$worksiteIds = AccountWorksite::where('account_id', $accountId)->column('worksite_id');
$total = 0;
$res = [
'total' => $total,
'current' => $page ?: 1,
'size' => $size ?: 20,
'info' => [],
'list' => new Collection(),
];
if (empty($worksiteIds)) {
return $this->json(0, 'success', $res);
}
// 当前明确说明 负责人和工地是一对一关系
$where[] = ['pml.worksite_id', '=', $worksiteIds[0]];
$query = \app\model\PayMonthLog::alias('pml')
// ->leftJoin('account a', 'a.id = pml.account_id')
->where($where);
// 汇总信息
$info = $query->fieldRaw('sum(pml.amount) as amount, sum(pml.base_amount) as base_amount,
sum(pml.overtime_amount) as overtime_amount')->find()->toArray();
// 已发放工资
$doneAmount = $query->where('pml.status', 1)->fieldRaw('sum(pml.amount) as amount')->find()->toArray();
$res['info'] = [
'amount' => $info['amount'] ?? 0,
'base_amount' => $info['base_amount'] ?? 0,
'overtime_amount' => $info['overtime_amount'] ?? 0,
'done_amount' => $doneAmount['amount'] ?? 0,
'not_amount' => Math::sub($info['amount'] ?? 0, $doneAmount['amount'] ?? 0),
];
if ($status >= 0) {
$where[] = ['pml.status', '=', $status];
}
$query = \app\model\PayMonthLog::alias('pml')
->leftJoin('account a', 'a.id = pml.account_id')
->where($where);
$total = $query->count();
$res['total'] = $total;
if ($total > 0) {
$res['list'] = $query->field('pml.id,pml.status,pml.amount,pml.base_amount,pml.overtime_amount,a.real_name as name')->page($page, $size)->order('pml.id', 'desc')->select();
$res['list']->each(function ($item) {
$item->status_text = $item->status == 1 ? '已发放' : '待发放';
});
}
return $this->json(0, 'success', $res);
}
}

View File

@ -0,0 +1,403 @@
<?php
namespace app\controller\api\v1;
use app\controller\api\Base;
use app\exception\RepositoryException;
use app\model\Account;
use app\model\AccountRecord;
use app\repository\AccountRepository;
use app\service\File;
use app\service\Jwt;
use app\service\wx\WechatApplets;
use app\validate\User as UserValidate;
use EasyWeChat\Kernel\Exceptions\InvalidConfigException;
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 User extends Base
{
protected $noNeedLogin = [
'login',
'tempLogin',
'signInfo',
'signToday',
'checkActive'
];
/**
* 登录 成功返回token及用户信息
*
* @return Json
* @throws InvalidConfigException
*/
public function login(): Json
{
$params = input();
Log::error('进入参数');
Log::error($params);
Log::error('参数结束');
$validate = new UserValidate();
if (!$validate->scene('wx_applets')->check($params)) {
return $this->json(4000, $validate->getError());
}
$minApp = WechatApplets::getInstance();
$jsCode = $params['code'];
$wxUser = $minApp->auth->session($jsCode);
if (isset($wxUser['errcode']) && $wxUser['errcode'] != 0) {
return $this->json(4001, $wxUser['errcode'].';'.$wxUser['errmsg'] ?? '登录失败');
}
// $wxUser success has [session_key, openid, unionid]
// 有效期2小时
$wxUser['expire_time'] = time() + 7200;
$wxUser['session_key'] = $wxUser['session_key'] ?? '';
$openID = $wxUser['openid'];
$unionid = $wxUser['unionid'] ?? '';
if (empty($openID) && empty($unionid)) {
return $this->json(4002, '登录失败');
}
$isActive = $params['is_active'] ?? 0;
$isActive = (is_numeric($isActive) && $isActive > 0) ? AccountRepository::BOOL_TRUE : AccountRepository::BOOL_FALSE;
$phoneActive = $params['phone_active'] ?? 0;
$phoneActive = (is_numeric($phoneActive) && $phoneActive > 0) ? AccountRepository::BOOL_TRUE : AccountRepository::BOOL_FALSE;
try {
$repo = AccountRepository::getInstance();
$account = $repo->findByOpenID($openID);
if (!$account && !empty($unionid)) {
$account = $repo->findByUnionId($unionid);
}
$now = date('Y-m-d H:i:s');
if (!$account) {
// 自动注册
$account = $repo->create([
'unionid' => $unionid ?? '',
'openid' => $openID,
'last_login' => $now,
'login_ip' => $this->request->ip(),
'created_at' => $now,
'nickname' => $params['nickname'] ?? '',
'headimgurl' => $params['headimgurl'] ?? '',
'mobile' => $params['mobile'] ?? '',
'status' => AccountRepository::STATUS_NORMAL,
'is_active' => $isActive,
'phone_active' => $phoneActive,
'session_key' => $wxUser['session_key'] ?? '',
]);
} else {
$updateData = [
'last_login' => date('Y-m-d H:i:s'),
'login_ip' => $this->request->ip(),
'session_key' => $wxUser['session_key'] ?? '',
];
// 更新资料
$modifyStringList = ['headimgurl', 'nickname', 'mobile'];
foreach ($modifyStringList as $modifyKey) {
if (isset($account[$modifyKey]) && empty($account[$modifyKey])) {
$updateData[$modifyKey] = $params[$modifyKey] ?? '';
}
}
if (empty($account['gender'])) {
$updateData['gender'] = $params['gender'] ?? 0;
}
if (isset($account['is_active']) && $account['is_active'] == AccountRepository::BOOL_FALSE) {
$updateData['is_active'] = $isActive;
}
if (isset($account['phone_active']) && $account['phone_active'] == AccountRepository::BOOL_FALSE) {
$updateData['phone_active'] = $phoneActive;
}
$repo->update($updateData, ['id' => $account['id']]);
$account = $repo->findById($account['id']);
}
} catch (RepositoryException | Exception $e) {
return $this->json(4003, '登录失败!'.$e->getMessage());
}
$account = $account->toArray();
$jwtData = [
'user_id' => $account['id'],
'open_id' => $openID,
'session_key' => $wxUser['session_key'],
'expire_time' => $wxUser['expire_time'],
];
$account['headimgurl'] = File::convertCompleteFileUrl($account['headimgurl']);
$fields = [
'coding', 'real_name', 'nickname', 'headimgurl', 'gender', 'mobile',
'status', 'is_active', 'phone_active'
];
$accountData = arrayKeysFilter($account, $fields);
$data = [
'account_id' => $account['id'],
'token' => Jwt::generate($jwtData),
'expire' => $wxUser['expire_time'],
'openid' => $openID,
];
$data = array_merge($data, $accountData);
return $this->json(0, 'success', $data);
}
/**
* 获取用户信息
*
* @return Json
*/
public function info(): Json
{
try {
$accountId = $this->request->user['user_id'] ?? 0;
$user = Account::getUser($accountId);
return $this->json(0, 'success', $user);
} catch (Exception $e) {
return $this->json(4000, '没有相关的用户记录'.$e->getMessage());
}
}
/**
* 修改用户信息
*/
public function updateInfo(): Json
{
try {
$params = input('post.');
$rules = [
'field|修改项' => 'require|in:nickname',
'value|修改内容' => 'require',
];
$accountId = $this->request->user['user_id'] ?? 0;
$validate = $this->validateByApi($params, $rules, ['field.in' => '参数错误']);
if ($validate !== true) {
return $validate;
}
if (!$customer = Account::findById($accountId)) {
return $this->json(4004, '用户不存在');
}
$customer->save([
$params['field'] => $params['value']
]);
} catch (Exception $e) {
Log::error('修改用户信息失败'.$e->getMessage());
return $this->json(5000, '修改失败!'.$e->getMessage());
}
return $this->json();
}
/**
* 临时登录 通过openid登录 仅用于接口测试阶段
*
* @return Json
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function tempLogin(): Json
{
$params = $this->request->param();
if (!isset($params['openid'])) {
return $this->json(4001, '参数错误');
}
if (!$user = AccountRepository::getInstance()->findByOpenID($params['openid'])) {
return $this->json(4004, '账号不存在');
}
$data = [
'token' => Jwt::generate(['user_id' => $user['id'], 'nickname' => $user['nickname']]),
'expire' => Jwt::expire()
];
return $this->json(0, 'success', $data);
}
/**
* 用户操作记录 分享、咨询
*
* @return Json
* @throws Exception
*/
public function record(): Json
{
if (!$this->request->isPost()) {
return $this->json(4000, '无效请求');
}
$accountId = $this->request->user['user_id'] ?? 0;
$params = input('post.');
$rules = [
'type|操作类型' => 'require|in:content,other,spu,activity',
'action|操作' => 'require',
];
if (isset($params['type']) && $params['type'] == AccountRecord::TYPE_CONTENT) {
$rules['id|ID'] = 'require';
}
$validate = $this->validateByApi($params, $rules, ['type.in' => '类型错误', 'id.require' => '此类型 ID必传']);
if ($validate !== true) {
return $validate;
}
try {
$relationId = $params['id'] ?? 0;
$relationId = is_numeric($relationId) ? $relationId : 0;
AccountRecord::record($accountId, $params['type'], $params['action'], $relationId);
} catch (Exception $e) {
AccountRepository::log('记录用户操作失败', $e);
return $this->json(5001, '操作失败');
}
return $this->json();
}
/**
* 绑定手机
*
* @return bool|Json
* @throws Exception
*/
public function bindPhone()
{
if (!$this->request->isPost()) {
return $this->json(4000, '无效请求');
}
$accountId = $this->request->user['user_id'] ?? 0;
$params = input('post.');
$rules = [
'encryptedData|加密数据' => 'require',
'iv|IV' => 'require',
];
$validate = $this->validateByApi($params, $rules);
if ($validate !== true) {
return $validate;
}
try {
if (!$account = Account::findById($accountId)) {
return $this->json(4000, '用户不存在');
}
// 解密手机相关数据 若存在手机则覆盖
$minApp = WechatApplets::getInstance();
$sessionKey = $this->request->user['session_key'] ?? '';
$decryptData = $minApp->encryptor->decryptData($sessionKey, $params['iv'], $params['encryptedData']);
$phone = $decryptData['phoneNumber'] ?? ''; // 通过iv和加密数据 解密出手机号
if (Account::where('id', '<>', $accountId)->where('mobile', $phone)->count() > 0) {
return $this->json(4000, '该手机已被绑定,若有绑定错误,请联系客服');
}
if ($phone) {
$account->save(['mobile' => $phone, 'phone_active' => Account::COMMON_ON]);
}
return $this->json(0, 'success', ['phone' => $phone]);
} catch (Exception $e) {
AccountRepository::log('手机绑定失败', $e);
return $this->json(5001, '手机绑定失败');
}
}
// 打卡页面信息
public function signInfo(): Json
{
$list = [];
$week = ['日', '一', '二', '三', '四', '五', '六'];
$info = [
'today' => date('Y年m月d日'),
'week' => '星期'.$week[date('w')],
'now' => date('H:i:s'),
'is_sign' => (int) !empty($list),
];
return $this->json(0, 'success', ['info' => $info]);
}
// 今日打卡记录
public function signToday(): Json
{
$accountId = $this->request->user['user_id'] ?? 0;
$list = [];
if ($accountId > 0) {
$where = [];
$where[] = ['cl.created_at', '>', date('Y-m-d 00:00:00')];
$where[] = ['cl.created_at', '<', date('Y-m-d 23:59:59')];
$where[] = ['cl.account_id', '=', $accountId];
$list = \app\model\ClockLog::alias('cl')
->leftJoin('worksite w', 'w.id = cl.worksite_id')
->field('cl.*,w.name as worksite_name')
->where($where)
->order('cl.id', 'desc')
->select();
$list->each(function ($item) {
$item->type_text = $item->type == 'in' ? '上班' : '下班';
switch ($item->status) {
case 0:
$item->status_text = '待确认';
break;
case 1:
$item->status_text = '已确认';
break;
case -1:
$item->status_text = '不通过';
break;
}
$item->time = date('H:i:s', $item->create_time);
});
$list = $list->toArray();
}
return $this->json(0, 'success', ['list' => $list]);
}
public function checkActive(): Json
{
try {
$openid = input('openid/s');
if (empty($openid)) {
return $this->json(0,'success', ['status' => 0]);
}
$isActive = (int)Account::where('openid', $openid)->value('is_active');
return $this->json(0, 'success', ['status' => $isActive]);
} catch (Exception $e) {
return $this->json(4000, '检查账号是否微信授权是不'.$e->getMessage());
}
}
}

View File

@ -0,0 +1,673 @@
<?php
namespace app\controller\api\v1;
use app\controller\api\Base;
use app\controller\manager\Clock;
use app\exception\RepositoryException;
use app\model\Account;
use app\model\AccountRecord;
use app\model\AccountWorksite;
use app\model\CheckLog;
use app\model\ClockLog;
use app\model\OvertimeLog;
use app\model\PayLog;
use app\model\Worksite;
use app\repository\AccountRepository;
use app\service\File;
use app\service\Jwt;
use app\service\wx\WechatApplets;
use app\validate\User as UserValidate;
use EasyWeChat\Kernel\Exceptions\InvalidConfigException;
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 Worker
* @package app\controller\api\v1
*/
class Worker extends Base
{
protected $noNeedLogin = [];
/**
* 注册工人资料
*/
public function register(): Json
{
if (!$this->request->isPost()) {
return $this->json(4000, '无效请求');
}
$accountId = $this->request->user['user_id'] ?? 0;
if (!$account = Account::findById($accountId)) {
return $this->json(6001, '请先登录');
}
if ($account['role'] != Account::COMMON_OFF) {
return $this->json(4003, '您无需提交资料');
}
if ($account['checking'] == Account::COMMON_ON) {
return $this->json(4002, '您已提交资料,等待审核中');
}
$post = input('post.');
$rules = [
'real_name|姓名' => 'require|max:50|min:2',
'mobile|手机号' => 'require',
'pay|工资' => 'require|number|min:0',
'emergency_contact|紧急联系人' => 'require',
'emergency_phone|紧急联系人电话' => 'require',
'bank_card_name|银行卡户名' => 'require',
'bank_card_number|银行卡号' => 'require',
'bank_name|开户行' => 'require|max:100',
'card_number|身份证' => 'require|max:20|min:15',
'position|岗位' => 'require|number',
'worksite_id|工地' => 'require|number',
];
$message = [
'worksite_id.number' => '工地必选',
'worksite_id.position' => '岗位必选',
];
$validate = $this->validateByApi($post, $rules, $message);
if ($validate !== true) {
return $validate;
}
$fields = [
'real_name', 'mobile', 'pay', 'emergency_contact', 'emergency_phone', 'bank_card_name', 'bank_card_number',
'bank_name', 'card_number', 'position', 'worksite_id'
];
$post = array_filter($post, function ($item, $key) use ($fields) {
return in_array($key, $fields);
}, ARRAY_FILTER_USE_BOTH);
$post['account_id'] = $accountId;
$post['is_register'] = CheckLog::COMMON_ON;
$post['created_at'] = date('Y-m-d H:i:s');
try {
CheckLog::create($post);
$account->save(['checking' => Account::COMMON_ON]);
} catch (Exception $e) {
return $this->json(5000, '资料录入失败');
}
return $this->json();
}
/**
* 修改用户信息 字段区分1.负责人审核 2.不需要审核 如nickname
*/
public function updateInfo(): Json
{
try {
$params = input('post.');
$rules = [
'field|修改项' => 'require',
'value|修改内容' => 'require',
];
$accountId = $this->request->user['user_id'] ?? 0;
$validate = $this->validateByApi($params, $rules);
if ($validate !== true) {
return $validate;
}
if (!$customer = Account::findById($accountId)) {
return $this->json(4004, '用户不存在');
}
$needCheckFields = Account::needCheckFields();
// 需要审核的字段
if (in_array($params['field'], $needCheckFields)) {
// 字段值未改变
if ($params['value'] == ($customer[$params['field']] ?? '')) {
return $this->json();
}
$insert = [];
$update = [];
foreach ($needCheckFields as $field) {
$insert[$field] = $field == $params['field'] ? $params['value'] : $customer[$field];
if ($field == $params['field']) {
$update[$field] = $params['value'];
}
}
if (!$checkLog = CheckLog::where('account_id', $accountId)->where('status', CheckLog::COMMON_OFF)
->find()) {
// 没有待审核的记录则新增
$insert['created_at'] = date('Y-m-d H:i:s');
$insert['account_id'] = $accountId;
$insert['worksite_id'] = $customer['worksite_id'];
CheckLog::create($insert);
} else {
$checkLog->save($update);
}
$customer->save(['checking' => Account::COMMON_ON]);
} else {
$customer->save([
$params['field'] => $params['value']
]);
}
} catch (Exception $e) {
Log::error('工人资料修改失败'.$e->getMessage());
return $this->json(5000, '修改资料失败!'.$e->getMessage());
}
return $this->json();
}
/**
* 工人打卡
*/
public function sign(): Json
{
try {
$input = input('post.');
$rules = [
'type|打卡类型' => 'require|in:in,out',
'lat|维度' => 'require',
'lng|经度' => 'require',
'worksite_id|工地' => 'require|number',
];
$validate = $this->validateByApi($input, $rules, ['type.in' => '打卡类型错误']);
if ($validate !== true) {
return $validate;
}
$accountId = $this->request->user['user_id'] ?? 0;
if (!$customer = Account::findById($accountId)) {
return $this->json(6001, '请先登录');
}
if ($customer['role'] != Account::ROLE_WORKER) {
return $this->json(4003, '完成审核后方可打卡');
}
$worksite = Worksite::getNearest($input['lng'], $input['lat']);
if (empty($worksite) || $worksite['id'] != $input['worksite_id']) {
return $this->json(4004, '定位错误,请在工地200米范围内打卡');
}
$time = time();
// $time = $time - 86401 * 3;
$now = date('Y-m-d H:i:s', $time);
$day = date('Ymd', $time);
if (ClockLog::where('account_id', $accountId)->where('type', $input['type'])->where('create_time', '>', time() - 60)->count()) {
return $this->json(4001, '打卡频率过快!');
}
ClockLog::create([
'account_id' => $accountId,
'type' => $input['type'],
'worksite_id' => $input['worksite_id'],
'created_at' => $now,
'create_time' => $time,
'day' => $day,
'indexs' => $accountId.'-'.$input['worksite_id'].'-'.$day,
]);
// 创建当日工资初始记录
PayLog::createWhenNotExists($accountId, $input['worksite_id'], $day);
} catch (Exception $e) {
Log::error('工人打卡失败'.$e->getMessage());
return $this->json(5000, '打卡失败!');
}
return $this->json();
}
// 我的打卡
public function clockList(): Json
{
$page = input('page/d', 1);
$size = input('size/d', 20);
$keyword = input('keyword/s');
$worksiteId = input('worksite_id/d', 0);
$begin = input('begin_at/s', '');
$end = input('end_at/s', '');
$accountId = $this->request->user['user_id'] ?? 0;
$where = [];
if (!empty($keyword)) {
$where[] = ['w.name', 'like', '%'.$keyword.'%'];
}
if ($worksiteId > 0) {
$where[] = ['cl.worksite_id', '=', $worksiteId];
}
if (!empty($begin)) {
$where[] = ['cl.created_at', '>', $begin.' 00:00:00'];
}
if (!empty($end)) {
$where[] = ['cl.created_at', '<', $end.' 23:59:59'];
}
$where[] = ['cl.account_id', '=', $accountId];
$query = \app\model\ClockLog::alias('cl')
->leftJoin('worksite w', 'w.id = cl.worksite_id')
->field('cl.*,w.name as worksite_name')
->where($where);
$total = $query->count();
$res = [
'total' => $total,
'current' => $page ?: 1,
'size' => $size ?: 20,
'list' => new Collection(),
];
if ($total > 0) {
$res['list'] = $query->page($page, $size)->order('cl.id', 'desc')->select();
$res['list']->each(function ($item) {
$item->type_text = $item->type == 'in' ? '上班' : '下班';
switch ($item->status) {
case 0:
$item->status_text = '待确认';
break;
case 1:
$item->status_text = '已确认';
break;
case -1:
$item->status_text = '不通过';
break;
}
$item->time = date('H:i:s', $item->create_time);
});
}
return $this->json(0, 'success', $res);
}
/**
* 提交加班
*/
public function overtime(): Json
{
try {
$input = input('post.');
$rules = [
'day|加班日期' => 'require|date',
'time|加班时长' => 'require|float',
'worksite_id|工地' => 'require|number',
];
$validate = $this->validateByApi($input, $rules, ['worksite_id.number' => '工地必传']);
if ($validate !== true) {
return $validate;
}
$accountId = $this->request->user['user_id'] ?? 0;
if (!$customer = Account::findById($accountId)) {
return $this->json(6001, '请先登录');
}
if ($customer['role'] != Account::ROLE_WORKER) {
return $this->json(4003, '还不是工人');
}
$time = time();
$now = date('Y-m-d H:i:s', $time);
$day = date('Ymd', strtotime($input['day']));
OvertimeLog::create([
'account_id' => $accountId,
'worksite_id' => $input['worksite_id'],
'day_text' => $input['day'],
'day' => $day,
'time' => $input['time'],
'remarks' => $input['remarks'] ?? '',
'indexs' => $accountId.'-'.$input['worksite_id'].'-'.$day,
'created_at' => $now,
'create_time' => $time,
]);
// 创建当日工资初始记录
PayLog::createWhenNotExists($accountId, $input['worksite_id'], $day);
} catch (Exception $e) {
Log::error('工人加班提交失败'.$e->getMessage());
return $this->json(5000, '加班申请失败!');
}
return $this->json();
}
// 我的加班记录
public function overtimeList(): Json
{
$page = input('page/d', 1);
$size = input('size/d', 20);
$keyword = input('keyword/s');
$worksiteId = input('worksite_id/d', 0);
$begin = input('begin_at/s', '');
$end = input('end_at/s', '');
$accountId = $this->request->user['user_id'] ?? 0;
$where = [];
if (!empty($keyword)) {
$where[] = ['w.name', 'like', '%'.$keyword.'%'];
}
if ($worksiteId > 0) {
$where[] = ['ol.worksite_id', '=', $worksiteId];
}
if (!empty($begin)) {
$where[] = ['ol.created_at', '>', $begin.' 00:00:00'];
}
if (!empty($end)) {
$where[] = ['ol.created_at', '<', $end.' 23:59:59'];
}
$where[] = ['ol.account_id', '=', $accountId];
$query = \app\model\OvertimeLog::alias('ol')
->leftJoin('worksite w', 'w.id = ol.worksite_id')
->field('ol.*,w.name as worksite_name')
->where($where);
$total = $query->count();
$res = [
'total' => $total,
'current' => $page ?: 1,
'size' => $size ?: 20,
'list' => new Collection(),
];
if ($total > 0) {
$res['list'] = $query->page($page, $size)->order('ol.id', 'desc')->select();
$res['list']->each(function ($item) {
switch ($item->status) {
case 0:
$item->status_text = '待确认';
break;
case 1:
$item->status_text = '已确认';
break;
case -1:
$item->status_text = '不通过';
break;
}
$item->create_time = date('Y年m月d日 H:i:s', $item->create_time);
$item->worktime = date('Y年m月d日', strtotime($item->day));
unset($item->check_at);
unset($item->check_by);
unset($item->created_at);
unset($item->worksite_id);
unset($item->is_statistic);
unset($item->day);
unset($item->day_text);
});
}
return $this->json(0, 'success', $res);
}
// 加班申请被打回后再次编辑
public function overtimeEdit(): Json
{
$id = input('id');
$input = input('post.');
$rules = [
'worksite_id|工地' => 'require|number',
'day|加班日期' => 'require|date',
'time|加班时长' => 'require|float',
];
$validate = $this->validateByApi($input, $rules);
if ($validate !== true) {
return $validate;
}
$accountId = $this->request->user['user_id'] ?? 0;
if (!$item = OvertimeLog::where('id', $id)->find()) {
return $this->json(4004, '记录不存在');
}
if ($item['account_id'] != $accountId) {
return $this->json(4003, '不是你提交的加班申请');
}
if ($item['status'] != OvertimeLog::STATUS_NO) {
return $this->json(4003, '当前状态不能编辑');
}
$item->save([
'day' => date('Ymd', strtotime($input['day'])),
'worksite_id' => $input['worksite_id'],
'time' => $input['time'],
'remarks' => $input['remarks'] ?? '',
'status' => OvertimeLog::COMMON_OFF,
]);
return $this->json(0, 'success');
}
// 加班申请被打回后删除
public function overtimeDel(): Json
{
$id = input('id');
$accountId = $this->request->user['user_id'] ?? 0;
if (!$item = OvertimeLog::where('id', $id)->find()) {
return $this->json(4004, '记录不存在');
}
if ($item['account_id'] != $accountId) {
return $this->json(4003, '不是你提交的加班申请');
}
if ($item['status'] != OvertimeLog::STATUS_NO) {
return $this->json(4003, '当前状态不能删除');
}
$item->delete();
return $this->json(0, 'success');
}
// 我的工资记录
public function payListMock(): Json
{
$page = input('page/d', 1);
$size = input('size/d', 20);
$accountId = $this->request->user['user_id'] ?? 0;
$where = [];
if (!empty($keyword)) {
$where[] = ['w.name', 'like', '%'.$keyword.'%'];
}
$where[] = ['cl.account_id', '=', $accountId];
// $query = \app\model\ClockLog::alias('cl')
// ->leftJoin('worksite w', 'w.id = cl.worksite_id')
// ->field('cl.*,w.name as worksite_name')
// ->where($where);
// $total = $query->count();
$total = 10;
$res = [
'total' => $total,
'current' => $page ?: 1,
'size' => $size ?: 20,
'list' => new Collection(),
];
if ($total > 0) {
$res['list'] = [
[
'status' => 0,
'status_text' => '待发放',
'date' => '2022年11月',
'base_amount' => 7500,
'overtime_amount' => 500,
'amount' => 8000,
],
[
'status' => 1,
'status_text' => '已发放',
'date' => '2022年10月',
'base_amount' => 7500,
'overtime_amount' => 500,
'amount' => 8000,
],
[
'status' => 0,
'status_text' => '已发放',
'date' => '2022年09月',
'base_amount' => 7500,
'overtime_amount' => 500,
'amount' => 8000,
],
[
'status' => 0,
'status_text' => '已发放',
'date' => '2022年08月',
'base_amount' => 7500,
'overtime_amount' => 0,
'amount' => 7500,
],
[
'status' => 0,
'status_text' => '已发放',
'date' => '2022年08月',
'base_amount' => 7500,
'overtime_amount' => 0,
'amount' => 7500,
],
[
'status' => 0,
'status_text' => '已发放',
'date' => '2022年07月',
'base_amount' => 7500,
'overtime_amount' => 0,
'amount' => 7500,
],
[
'status' => 0,
'status_text' => '已发放',
'date' => '2022年06月',
'base_amount' => 7500,
'overtime_amount' => 0,
'amount' => 7500,
],
];
}
return $this->json(0, 'success', $res);
}
// 我的工资记录
public function payList(): Json
{
$page = input('page/d', 1);
$size = input('size/d', 20);
$accountId = $this->request->user['user_id'] ?? 0;
$where = [];
$where[] = ['pml.account_id', '=', $accountId];
$query = \app\model\PayMonthLog::alias('pml')
->leftJoin('account a', 'a.id = pml.account_id')
->where($where)
->group('pml.time')
->fieldRaw('pml.time,pml.year,pml.month,sum(pml.amount) as amount,sum(pml.base_amount) as base_amount,sum(pml.overtime_amount) as overtime_amount');
$total = $query->count();
$res = [
'total' => $total,
'current' => $page ?: 1,
'size' => $size ?: 20,
'list' => new Collection(),
];
if ($total > 0) {
// 获取按月已发工资
$paidMonth = \app\model\PayMonthLog::alias('pml')
->leftJoin('account a', 'a.id = pml.account_id')
->where($where)
->where('pml.status', 1)
->group('pml.time')
->page($page, $size)
->order('time', 'desc')
->fieldRaw('sum(pml.amount) as amount,pml.time')
->select()->toArray();
$monthPay = [];
foreach ($paidMonth as $p) {
$monthPay[$p['time']] = $p['amount'];
}
$res['list'] = $query->page($page, $size)->order('time', 'desc')->select();
$res['list']->each(function ($item) use ($monthPay) {
$item->date = $item['year'].'年'.$item['month'].'月';
$item->done = $monthPay[$item->time] ?? 0;
$item->status = 0;
$item->status_text = '待发放';
if ($item->amount <= $item->done) {
$item->status = 1;
$item->status_text = '已发放';
}
unset($item->year);
unset($item->month);
unset($item->think_count);
});
}
return $this->json(0, 'success', $res);
}
// 获取审核记录
public function checkDetail(): Json
{
$id = input('id');
if (!$item = CheckLog::where('id', $id)->find()) {
return $this->json(4004, '记录不存在');
}
$item = arrayNullToString($item->toArray());
return $this->json(0, 'success', $item);
}
}

View File

@ -0,0 +1,167 @@
<?php
namespace app\controller\api\v1\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 $videoUploadPath;
// 文件上传对外默认保存目录是否有写权限
private bool $uploadPathIsWritable = false;
private $videoUploadPathIsWritable = 0;
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');
$this->videoUploadPath = Config::get('filesystem.disks.video.url');
if (!is_writable(app()->getRootPath().'public'.$this->uploadPath)) {
mkdir(app()->getRootPath().'public'.$this->uploadPath, 0777, true);
}
$this->uploadPathIsWritable = 1;
if (!is_writable(app()->getRootPath().'public'.$this->videoUploadPath)) {
mkdir(app()->getRootPath().'public'.$this->videoUploadPath, 0777, true);
}
$this->videoUploadPathIsWritable = 1;
$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;
$return['full_src'] = resourceJoin($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);
}
}
/**
* 通用视频上传
* @return Json
*/
public function video()
{
try {
$file = request()->file('video');
$md5 = $file->md5();//文件md5
if ($this->validate->checkVideo($file)) {
if (!$this->videoUploadPathIsWritable) {
throw new \Exception('上传文件夹需要写入权限');
}
$src = Filesystem::putFile('videos/'.date('Ym'), $file, 'uniqid');
$src = $this->uploadPath.'/'.$src;
$return['src'] = $src;
$return['full_src'] = resourceJoin($src);
//加入上传文件表
File::add($file, $src, $md5, 'video');
return $this->json(0, 'success', $return);
} else {
$errorMsg = Lang::get($this->validate->getError());
return $this->json(4002, $errorMsg);
}
} catch (\Exception $e) {
return $this->json(4000, $e->getMessage());
}
}
}

View File

@ -0,0 +1,507 @@
<?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/base', 'base');
$baseConfig = config('base');
$config = $this->ossConfig();
$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();
}
protected function ossConfig(string $provider = 'qiniu')
{
Config::load('extra/'.$provider, $provider);
return config($provider);
}
/**
* 视频列表
*
* @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/base', 'base');
$baseConfig = config('base');
$config = $this->ossConfig();
$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()
{
$baseConfig = config('base');
$config = $this->ossConfig();
$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);
}
}

105
app/controller/manager/Base.php Executable file
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,224 @@
<?php
declare (strict_types=1);
namespace app\controller\manager;
use app\model\ClockLog;
use Exception;
use app\model\Log;
use think\Collection;
use think\response\View;
use think\response\Json;
use think\db\exception\DbException;
use think\exception\ValidateException;
use think\db\exception\DataNotFoundException;
use think\db\exception\ModelNotFoundException;
class Clock extends Base
{
protected $noNeedLogin = ['index', 'add', 'edit', 'del', 'modify'];
/**
* 列表
*
* @throws Exception
*/
public function index()
{
$accountId = input('user_id/d', 0);
if ($this->request->isPost()) {
$params = input('searchParams/a');
$page = input('page/d', 1);
$size = input('size/d', 20);
$where = [];
if (!empty($params)) {
foreach ($params as $key => $param) {
$param = trim($param);
if ($key == 'keyword') {
$where[] = ['w.name|a.nickname|a.mobile|a.real_name|a2.nickname|a2.real_name', 'like', '%'.$param.'%'];
continue;
}
if ($param == '0' || !empty($param)) {
$where[] = ['cl.'.$key, '=', $param];
}
}
}
if ($accountId > 0) {
$where[] = ['cl.account_id', '=', $accountId];
}
$query = ClockLog::alias('cl')
->leftJoin('account a', 'a.id = cl.account_id')
->leftJoin('account a2', 'a2.id = cl.check_by')
->leftJoin('worksite w', 'w.id = cl.worksite_id')
->where($where);
$total = $query->count();
$res = [
'total' => $total,
'current' => $page ?: 1,
'size' => $size ?: 20,
'list' => [],
];
if ($total > 0) {
$typeText = ClockLog::typeText();
$statusText = ClockLog::statusText();
$res['list'] = $query->fieldRaw('cl.*,a.nickname,a.real_name,a.mobile,w.name as worksite_name,a2.nickname as check_nickname,a2.real_name as check_name')->page($page, $size)->order('cl.id', 'desc')->select();
$res['list']->each(function ($item) use ($typeText, $statusText) {
$item->type_text = $typeText[$item->type] ?? '其他';
$item->status_text = $statusText[$item->status] ?? '其他';
$item->account_text = $item->nickname.'-'.$item->real_name.'-'.$item->mobile;//用户信息
$item->check_text = $item->check_by ? $item->check_nickname.'-'.$item->check_name : '';//审核人信息
});
}
return $this->json(0, 'success', $res);
}
$this->data['userId'] = $accountId;
$this->data['worksiteList'] = \app\model\Worksite::list();
return $this->view();
}
/**
* 添加
*
* @return Json|View
*/
public function add()
{
if ($this->request->isPost()) {
try {
$input = input('post.');
if (!isset($input['name'])) {
return $this->json(4000, '参数错误');
}
ClockLog::create([
'name' => $input['name'] ?? '',
]);
return $this->json();
} catch (Exception $e) {
return $this->json(4001, '添加失败'.$e->getMessage());
}
}
return $this->view();
}
/**
* 编辑
*
* @return \think\response\Json|\think\response\View
*/
public function edit()
{
$id = input('id');
//通过ID查询
$item = ClockLog::where('id', (int) $id)->find();
if (empty($item)) {
return $this->json(4000, '没有相关记录!');
}
if ($this->request->isPost()) {
try {
$input = input('post.');
if (!isset($input['name'])) {
return $this->json(4000, '参数错误');
}
$item->save([
'name' => $input['name'] ?? '',
]);
return $this->json();
} catch (Exception $e) {
return $this->json(5000, $e->getMessage());
}
}
$this->data['item'] = $item;
$this->data['id'] = $id;
return $this->view();
}
/**
* 更新属性
*
* @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;
}
// 通过ID查询
if (!$info = ClockLog::where('id', (int) $item['id'])->find()) {
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 \think\response\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)) {
//删除逻辑
if (\app\model\Account::whereIn('ClockLog', $ids)->count() > 0) {
return $this->json(4000, '所选岗位已分配给用户,请先移除后再删除');
}
ClockLog::whereIn('id', $ids)->delete();
Log::write(get_class(), 'del', '删除操作涉及到的ID为'.implode(',', $ids));
}
} catch (Exception $e) {
return $this->json(5000, $e->getMessage());
}
return $this->json();
}
}

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/'.unCamelize($name, '_'));
}
}
}

View File

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

143
app/controller/manager/File.php Executable file
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();
}
}

272
app/controller/manager/Goods.php Executable file
View File

@ -0,0 +1,272 @@
<?php
declare (strict_types=1);
namespace app\controller\manager;
use app\model\Account;
use app\model\GoodsArea;
use app\model\GoodsCategory;
use Exception;
use app\model\Log;
use think\Collection;
use think\response\View;
use think\response\Json;
use think\db\exception\DbException;
use think\exception\ValidateException;
use think\db\exception\DataNotFoundException;
use think\db\exception\ModelNotFoundException;
class Goods extends Base
{
protected $noNeedLogin = ['searchAccount'];
/**
* 列表
*
* @throws Exception
*/
public function index()
{
if ($this->request->isPost()) {
$params = input('searchParams/a');
$page = input('page/d', 1);
$size = input('size/d', 20);
$where = [];
if (!empty($params)) {
foreach ($params as $key => $param) {
$param = trim($param);
if ($key == 'keyword') {
$where[] = ['a.nickname|g.title|gc.title|g.phone|ga.title', 'like', '%'.$param.'%'];
continue;
}
if ($param == '0' || !empty($param)) {
$where[] = ['g.'.$key, 'like', '%'.$param.'%'];
}
}
}
$query = \app\model\Goods::alias('g')
->leftJoin('goods_category gc', 'gc.id = g.category_id')
->leftJoin('account a', 'a.id = g.account_id')
->leftJoin('goods_area ga', 'ga.id = g.area_id')
->field('g.*,gc.title as category_title,a.nickname,ga.title as area_name')
->where($where);
$total = $query->count();
$res = [
'total' => $total,
'current' => $page ?: 1,
'size' => $size ?: 20,
'list' => new Collection(),
];
if ($total > 0) {
$res['list'] = $query->page($page, $size)->order('g.sort', 'desc')->order('g.id', 'desc')->select();
}
return $this->json(0, 'success', $res);
}
return $this->view();
}
/**
* 添加
*
* @return Json|View
*/
public function add()
{
if ($this->request->isPost()) {
try {
$input = input('post.');
// $images = input('img_images/a', []);
\app\model\Goods::create([
'title' => $input['title'],
'cover' => $input['cover'],
'category_id' => $input['category_id'],
'content' => $input['content'] ?? '',
'phone' => $input['phone'] ?? '',
'price' => $input['price'] ?? 0,
'original_price' => $input['original_price'] ?? $input['price'],
'account_id' => $input['account_id'] ?? 0,
'status' => $input['status'] ?? 0,
'area_id' => $input['area_id'] ?? 0,
'images' => $input['images'] ?? '',
]);
return $this->json();
} catch (Exception $e) {
return $this->json(4001, '添加失败'.$e->getMessage());
}
}
$this->data['cateList'] = GoodsCategory::order('sort', 'desc')
->order('id', 'desc')
->column('id,title');
$this->data['areaList'] = GoodsArea::order('sort', 'desc')
->order('id', 'desc')
->column('id,title');
return $this->view();
}
/**
* 编辑
*
* @return \think\response\Json|\think\response\View
*/
public function edit()
{
$id = input('id');
//通过ID查询
$item = \app\model\Goods::findById((int) $id);
if (empty($item)) {
return $this->json(4000, '没有相关的记录!');
}
if ($this->request->isPost()) {
try {
$input = input('post.');
$item->save([
'title' => $input['title'],
'cover' => $input['cover'],
'category_id' => $input['category_id'],
'content' => $input['content'],
'phone' => $input['phone'],
'price' => $input['price'],
'original_price' => $input['original_price'],
'account_id' => $input['account_id'] ?? 0,
'status' => $input['status'] ?? 0,
'area_id' => $input['area_id'] ?? 0,
'images' => $input['images'] ?? '',
]);
return $this->json();
} catch (Exception $e) {
return $this->json(5000, $e->getMessage());
}
}
$bindAccount = Account::where('id', $item['account_id'])->column('id,nickname,mobile');
$accountList = [];
foreach ($bindAccount as $ac) {
$accountList[] = [
'account_desc' => $ac['nickname'].'【手机:'.$ac['mobile'].'】',
'id' => $ac['id'], 'selected' => true
];
}
$this->data['jsonStr'] = $bindAccount ? json_encode($accountList, JSON_UNESCAPED_UNICODE) : json_encode([]);
$this->data['item'] = $item;
$this->data['id'] = $id;
$this->data['cateList'] = GoodsCategory::order('sort', 'desc')
->order('id', 'desc')
->column('id,title');
$this->data['areaList'] = GoodsArea::order('sort', 'desc')
->order('id', 'desc')
->column('id,title');
return $this->view();
}
/**
* 更新属性
*
* @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;
}
// 通过ID查询
if (!$info = \app\model\Goods::findById((int) $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 \think\response\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)) {
\app\model\Goods::whereIn('id', $ids)->delete();
Log::write(get_class(), 'del', '删除操作涉及到的ID为'.implode(',', $ids));
}
} catch (Exception $e) {
return $this->json(5000, $e->getMessage());
}
return $this->json();
}
/**
* 搜索用户
*/
public function searchAccount(): Json
{
$keyword = input('keyword/s', '');
$list = [];
if (!empty($keyword)) {
$res = Account::findList([], ['id', 'nickname', 'mobile'], 1, 1000, function ($q) use ($keyword) {
return $q->where('nickname', 'like', '%'.$keyword.'%')
->whereOr('mobile', 'like', '%'.$keyword.'%');
});
$list = $res['list']->toArray();
foreach ($list as &$item) {
$item['account_desc'] = $item['nickname'].'【手机:'.$item['mobile'].'】';
}
unset($item);
}
return $this->json(0, 'success', $list);
}
}

View File

@ -0,0 +1,206 @@
<?php
declare (strict_types = 1);
namespace app\controller\manager;
use Exception;
use app\model\Log;
use think\Collection;
use think\response\View;
use think\response\Json;
use app\controller\manager\Base;
use think\db\exception\DbException;
use think\exception\ValidateException;
use think\db\exception\DataNotFoundException;
use think\db\exception\ModelNotFoundException;
class GoodsCategory extends Base
{
protected $noNeedLogin = ['index', 'add', 'edit', 'del', 'modify'];
/**
* 列表
*
* @throws Exception
*/
public function index()
{
if ($this->request->isPost()) {
$params = input('searchParams/a');
$page = input('page/d', 1);
$size = input('size/d', 20);
$where = [];
if (!empty($params)) {
foreach ($params as $key => $param) {
$param = trim($param);
if ($key == 'keyword') {
$where[] = ['title', 'like', '%'.$param.'%'];
continue;
}
if ($param == '0' || !empty($param)) {
$where[] = [$key, 'like', '%'.$param.'%'];
}
}
}
$query = \app\model\GoodsCategory::where($where);
$total = $query->count();
$res = [
'total' => $total,
'current' => $page ?: 1,
'size' => $size ?: 20,
'list' => new Collection(),
];
if ($total > 0) {
$res['list'] = $query->page($page, $size)->order('sort', 'desc')->order('id', 'desc')->select();
}
return $this->json(0, 'success', $res);
}
return $this->view();
}
/**
* 添加
*
* @return Json|View
*/
public function add()
{
if ($this->request->isPost()) {
try {
$input = input('post.');
if (!isset($input['title'])) {
return $this->json(4000, '参数错误');
}
\app\model\GoodsCategory::create([
'title' => $input['title'] ?? '',
]);
return $this->json();
} catch (Exception $e) {
return $this->json(4001, '添加失败'.$e->getMessage());
}
}
return $this->view();
}
/**
* 编辑
*
* @return \think\response\Json|\think\response\View
*/
public function edit()
{
$id = input('id');
//通过ID查询
$item = \app\model\GoodsCategory::where('id', (int)$id)->find();
if (empty($item)) {
return $this->json(4000, '没有相关记录!');
}
if ($this->request->isPost()) {
try {
$input = input('post.');
if (!isset($input['title'])) {
return $this->json(4000, '参数错误');
}
$item->save([
'title' => $input['title'] ?? '',
]);
return $this->json();
} catch (Exception $e) {
return $this->json(5000, $e->getMessage());
}
}
$this->data['item'] = $item;
$this->data['id'] = $id;
return $this->view();
}
/**
* 更新属性
*
* @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;
}
// 通过ID查询
if (!$info = \app\model\GoodsCategory::where('id', (int)$item['id'])->find()) {
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 \think\response\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)) {
//删除逻辑
if (\app\model\Goods::whereIn('area_id', $ids)->count() > 0) {
return $this->json(4000, '所选分类下存在发布物品,请先移除物品后再删除');
}
\app\model\GoodsCategory::whereIn('id', $ids)->delete();
Log::write(get_class(), 'del', '删除操作涉及到的ID为'.implode(',', $ids));
}
} catch (Exception $e) {
return $this->json(5000, $e->getMessage());
}
return $this->json();
}
}

View File

@ -0,0 +1,98 @@
<?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()) {
return $this->json(0, '操作成功', []);
}
$this->data['count'] = 0;
return $this->view();
}
/**
* 菜单初始化
*
* @return Json
*/
public function init(): Json
{
\think\facade\Config::load('extra/base', 'base');
$config = config('base');
$title = $config['admin_title'] ?: '大向天诚商城管理系统';
// $title = mb_strlen($title) > 3 ? mb_substr($title, 0, 3).'...' : $title;
$res = [];
$res['homeInfo'] = ['title' => '控制台', 'href' => "manager/index/dashboard"];
$res['logoInfo'] = [
'title' => $title,
'href' => "",
'image' => $config['logo'] ?: '/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);
}
}

56
app/controller/manager/Log.php Executable file
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,80 @@
<?php
namespace app\controller\manager;
use app\service\Jwt;
use Exception;
use app\model\{Member, 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'],
'is_teacher' => $member['is_teacher'],
'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 = [];
\think\facade\Config::load('extra/base', 'base');
$config = config('base');
$viewData['loginTitle'] = $config['login_title'] ?? '大向天诚商城管理系统';
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'));
}
}

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

@ -0,0 +1,390 @@
<?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.'%'];
}
}
}
if ($this->auth['username'] != 'admin') {
$where[] = ['username', '<>', 'admin'];
}
$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);
}
}

263
app/controller/manager/Menu.php Executable file
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,382 @@
<?php
namespace app\controller\manager;
use app\exception\RepositoryException;
use app\model\Account;
use app\model\Account as AccountModel;
use app\model\PayMonthLog;
use app\model\Position;
use app\model\Worksite;
use app\repository\AccountRepository;
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 Pay
* @package app\controller\manager
*/
class Pay extends Base
{
protected $noNeedLogin = ['getAccountList', 'list'];
/**
* 详情
*
* @return View
* @throws RepositoryException
*/
public function detail(): View
{
$id = input('id/d', 0);
$item = PayMonthLog::findById($id);
$orderNum = 0;
$orderScoreNum = 0;
$totalPrice = 0;
$totalScore = 0;
$item['total_price'] = Math::fen2Yuan($totalPrice);
$item['order_num'] = $orderNum;
$item['order_score_num'] = $orderScoreNum;
$item['order_newest'] = [];
$this->data['item'] = $item;
return $this->view();
}
/**
* 编辑
*
* @return Redirect|Json|View
* @throws Exception
*/
public function edit()
{
$id = input('id/d', 0);
if (!$info = PayMonthLog::findById($id)) {
if ($this->request->isPost()) {
return $this->json(4000, '记录不存在');
} else {
return $this->error('记录不存在');
}
}
$all = PayMonthLog::where('account_id', $info['account_id'])->where('time', $info['time'])
->fieldRaw('sum(amount) as amount,sum(base_amount) as base_amount,sum(overtime_amount) as overtime_amount')
->find();
$info['amount'] = $all['amount'];
$info['base_amount'] = $all['base_amount'];
$info['overtime_amount'] = $all['overtime_amount'];
if ($this->request->isPost()) {
$item = input('post.');
$validate = $this->validateByApi($item, [
'status' => 'require',
]);
if ($validate !== true) {
return $validate;
}
try {
PayMonthLog::where('account_id', $info['account_id'])->where('time', $info['time'])->save(['status' => $item['status']]);
return $this->json();
} catch (ValidateException $e) {
return $this->json(4001, $e->getError());
}
}
$this->data['item'] = $info;
$this->data['account'] = Account::findById($info['account_id']);
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 = PayMonthLog::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()
{
$accountId = input('user_id/d', 0);
$worksite = Worksite::list();
$position = Position::list();
if ($this->request->isPost()) {
$page = input('page/d', 1);
$size = input('size/d', 20);
$searchParams = input('searchParams');
$search = [];
if ($searchParams) {
foreach ($searchParams as $key => $param) {
if ($param || $param == '0') {
switch ($key) {
case 'keyword':
$search[] = ['a.nickname|a.real_name|a.mobile', 'like', '%'.$param.'%'];
break;
default:
$search[] = ['pml.'.$key, '=', $param];
}
}
}
}
if ($accountId > 0) {
$search[] = ['pml.account_id', '=', $accountId];
}
try {
$total = 0;
$res = [
'total' => $total,
'current' => $page ?: 1,
'size' => $size ?: 20,
'info' => [],
'list' => [],
];
$query = PayMonthLog::alias('pml')
->leftJoin('account a', 'a.id = pml.account_id')
->where($search);
// 汇总信息
$info = $query->fieldRaw('sum(pml.amount) as amount, sum(pml.base_amount) as base_amount,
sum(pml.overtime_amount) as overtime_amount')->find();
// 已发放工资
$doneAmount = $query->where('pml.status', 1)->fieldRaw('sum(pml.amount) as amount')->find();
$res['info'] = [
'amount' => $info['amount'] ?? 0,
'base_amount' => $info['base_amount'] ?? 0,
'overtime_amount' => $info['overtime_amount'] ?? 0,
'done_amount' => $doneAmount['amount'] ?? 0,
'not_amount' => Math::sub($info['amount'] ?? 0, $doneAmount['amount'] ?? 0),
];
$query = PayMonthLog::alias('pml')
->leftJoin('account a', 'a.id = pml.account_id')
->leftJoin('worksite w', 'w.id = a.worksite_id')
->leftJoin('position p', 'p.id = a.position')
->where($search)
->group('account_id,time');
$total = $query->count();
$res['total'] = $total;
if ($total > 0) {
$res['list'] = $query->field('pml.id,sum(pml.amount) as amount,sum(pml.base_amount) as base_amount,pml.status,pml.year,pml.month,
sum(pml.overtime_amount) as overtime_amount,pml.account_id,pml.time,a.nickname,a.real_name,a.mobile,w.name as worksite_name,
p.name as position_text')
->page($page, $size)->order('pml.id', 'desc')->select();
$res['list']->each(function ($item) {
$item->status_text = $item->status == 1 ? '已发放' : '待发放';
$item->time_text = $item->year.'年'.$item->month.'月';
});
}
return $this->json(0, 'success', $res);
} catch (RepositoryException $e) {
return $this->json(4001, $e->getMessage());
} catch (Exception $e) {
return $this->json(5001, '获取工资列表失败'.$e->getMessage());
}
}
$this->data['userId'] = $accountId;
$this->data['worksiteList'] = $worksite;
$this->data['positionList'] = $position;
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, '非法请求');
}
/**
* 每月工资列表 与index的区别是 index按人与月份的汇总 几个工地的工资都进行了汇总
* list则是按每个工地单独统计
*
* @return View|Json
* @throws Exception
*/
public function list()
{
$accountId = input('user_id/d', 0);
$time = input('time/d', 0);
$worksite = Worksite::list();
$position = Position::list();
if ($this->request->isPost()) {
$page = input('page/d', 1);
$size = input('size/d', 20);
$searchParams = input('searchParams');
$search = [];
if ($searchParams) {
foreach ($searchParams as $key => $param) {
if ($param || $param == '0') {
switch ($key) {
case 'keyword':
$search[] = ['a.nickname|a.real_name|a.mobile', 'like', '%'.$param.'%'];
break;
default:
$search[] = ['pml.'.$key, '=', $param];
}
}
}
}
if ($accountId > 0) {
$search[] = ['pml.account_id', '=', $accountId];
}
if ($time > 0) {
$search[] = ['pml.time', '=', $time];
}
try {
$total = 0;
$res = [
'total' => $total,
'current' => $page ?: 1,
'size' => $size ?: 20,
'info' => [],
'list' => [],
];
$query = PayMonthLog::alias('pml')
->leftJoin('account a', 'a.id = pml.account_id')
->where($search);
// 汇总信息
$info = $query->fieldRaw('sum(pml.amount) as amount, sum(pml.base_amount) as base_amount,
sum(pml.overtime_amount) as overtime_amount')->find();
// 已发放工资
$doneAmount = $query->where('pml.status', 1)->fieldRaw('sum(pml.amount) as amount')->find();
$res['info'] = [
'amount' => $info['amount'] ?? 0,
'base_amount' => $info['base_amount'] ?? 0,
'overtime_amount' => $info['overtime_amount'] ?? 0,
'done_amount' => $doneAmount['amount'] ?? 0,
'not_amount' => Math::sub($info['amount'] ?? 0, $doneAmount['amount'] ?? 0),
];
$query = PayMonthLog::alias('pml')
->leftJoin('account a', 'a.id = pml.account_id')
->leftJoin('worksite w', 'w.id = pml.worksite_id')//注意此处需要使用pml表
->leftJoin('position p', 'p.id = a.position')
->where($search);
$total = $query->count();
$res['total'] = $total;
if ($total > 0) {
$res['list'] = $query->field('pml.*,a.nickname,a.real_name,a.mobile,w.name as worksite_name,
p.name as position_text')
->page($page, $size)->order('pml.id', 'desc')->select();
$res['list']->each(function ($item) {
$item->status_text = $item->status == 1 ? '已发放' : '待发放';
$item->time_text = $item->year.'年'.$item->month.'月';
});
}
return $this->json(0, 'success', $res);
} catch (RepositoryException $e) {
return $this->json(4001, $e->getMessage());
} catch (Exception $e) {
return $this->json(5001, '获取工资列表2失败'.$e->getMessage());
}
}
$this->data['userId'] = $accountId;
$this->data['time'] = $time;
$this->data['worksiteList'] = $worksite;
$this->data['positionList'] = $position;
return $this->view();
}
}

View File

@ -0,0 +1,204 @@
<?php
declare (strict_types = 1);
namespace app\controller\manager;
use Exception;
use app\model\Log;
use think\Collection;
use think\response\View;
use think\response\Json;
use think\db\exception\DbException;
use think\exception\ValidateException;
use think\db\exception\DataNotFoundException;
use think\db\exception\ModelNotFoundException;
class Position extends Base
{
protected $noNeedLogin = ['index', 'add', 'edit', 'del', 'modify'];
/**
* 列表
*
* @throws Exception
*/
public function index()
{
if ($this->request->isPost()) {
$params = input('searchParams/a');
$page = input('page/d', 1);
$size = input('size/d', 20);
$where = [];
if (!empty($params)) {
foreach ($params as $key => $param) {
$param = trim($param);
if ($key == 'keyword') {
$where[] = ['name', 'like', '%'.$param.'%'];
continue;
}
if ($param == '0' || !empty($param)) {
$where[] = [$key, 'like', '%'.$param.'%'];
}
}
}
$query = \app\model\Position::where($where);
$total = $query->count();
$res = [
'total' => $total,
'current' => $page ?: 1,
'size' => $size ?: 20,
'list' => new Collection(),
];
if ($total > 0) {
$res['list'] = $query->page($page, $size)->order('sort', 'desc')->order('id', 'desc')->select();
}
return $this->json(0, 'success', $res);
}
return $this->view();
}
/**
* 添加
*
* @return Json|View
*/
public function add()
{
if ($this->request->isPost()) {
try {
$input = input('post.');
if (!isset($input['name'])) {
return $this->json(4000, '参数错误');
}
\app\model\Position::create([
'name' => $input['name'] ?? '',
]);
return $this->json();
} catch (Exception $e) {
return $this->json(4001, '添加失败'.$e->getMessage());
}
}
return $this->view();
}
/**
* 编辑
*
* @return \think\response\Json|\think\response\View
*/
public function edit()
{
$id = input('id');
//通过ID查询
$item = \app\model\Position::where('id', (int)$id)->find();
if (empty($item)) {
return $this->json(4000, '没有相关记录!');
}
if ($this->request->isPost()) {
try {
$input = input('post.');
if (!isset($input['name'])) {
return $this->json(4000, '参数错误');
}
$item->save([
'name' => $input['name'] ?? '',
]);
return $this->json();
} catch (Exception $e) {
return $this->json(5000, $e->getMessage());
}
}
$this->data['item'] = $item;
$this->data['id'] = $id;
return $this->view();
}
/**
* 更新属性
*
* @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;
}
// 通过ID查询
if (!$info = \app\model\Position::where('id', (int)$item['id'])->find()) {
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 \think\response\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)) {
//删除逻辑
if (\app\model\Account::whereIn('position', $ids)->count() > 0) {
return $this->json(4000, '所选岗位已分配给用户,请先移除后再删除');
}
\app\model\Position::whereIn('id', $ids)->delete();
Log::write(get_class(), 'del', '删除操作涉及到的ID为'.implode(',', $ids));
}
} catch (Exception $e) {
return $this->json(5000, $e->getMessage());
}
return $this->json();
}
}

View File

@ -0,0 +1,277 @@
<?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 = [])
{
$username = session('auth')['username'] ?? '';//非超级管理员admin 部分展示权限
$menus = Menu::field("id,pid,title,sort")
->where('status', Menu::STATUS_NORMAL)
->when($username != 'admin', function ($q) {
$q->where('show_role', 1);
})
->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);
}
$this->data['username'] = $this->auth['username'] ?? '';
return $this->view();
}
}

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

@ -0,0 +1,282 @@
<?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);
event('UpdateBanner');
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);
event('UpdateBanner');
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);
event('UpdateBanner');
} 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);
event('UpdateBanner');
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();
}
}

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

@ -0,0 +1,230 @@
<?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 initialize()
{
$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)) {
mkdir(app()->getRootPath().'public'.$this->uploadPath, 0777, true);
}
$this->uploadPathIsWritable = 1;
if (!is_writable(app()->getRootPath().'public'.$this->videoUploadPath)) {
mkdir(app()->getRootPath().'public'.$this->videoUploadPath, 0777,true);
}
$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');
try {
if ($this->validate->checkVideo($video)) {
if (!$this->videoUploadPathIsWritable) {
throw new \Exception('上传文件夹需要写入权限');
}
$md5 = $video->md5();//文件md5
$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);
}
} catch (RepositoryException $e) {
return $this->json(1, $e->getMessage());
}
}
//文件上传(通用)
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 uploadImage($image)
{
// 字段名 image-image避免冲突 layui组件自动生成的隐藏file input框中name容易重名冲突
$md5 = $image->md5();//文件md5
$path = request()->param('path/s', '');//指定路径 基于public下 若为空则默认
$hasPath = !empty($path);
if ($this->validate->checkImage($image)) {
// if ($fileItem = File::where('md5', $md5)->find()) {
// $return['src'] = $fileItem['src'];
// $fileItem['updated_at'] = date('Y-m-d H:i:s');
//
// return $this->json(200, '该文件已存在 路径为:'.$fileItem['path'], $return);
// }
try {
if (!$this->uploadPathIsWritable) {
throw new \Exception('上传文件夹需要写入权限');
}
// 去除以/storage/开头的部分 如/storage/20210808/test => 20210808/test
if (strpos(trim($path, $this->DIRECTORY_SEPARATOR), \app\model\Attachment::ROOT_NAME.$this->DIRECTORY_SEPARATOR) === 0) {
$path = substr(trim($path, $this->DIRECTORY_SEPARATOR), strlen(\app\model\Attachment::ROOT_NAME.$this->DIRECTORY_SEPARATOR));
}
$datePath = $hasPath ? $path : 'images'.$this->DIRECTORY_SEPARATOR.date('Ym');//自定义目录
$datePath = ($datePath == $this->DIRECTORY_SEPARATOR."storage") ? $this->DIRECTORY_SEPARATOR : $datePath;
$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);
}
}
//富文本编辑器上传图片
public function tinyImage()
{
$image = request()->file('file');
if (!$image) {
// 字段名不对
header("HTTP/1.1 404 config field error");
exit;
}
if (!$this->uploadPathIsWritable) {
header("HTTP/1.1 403 Insufficient folder permissions");
exit;
} else {
$md5 = $image->md5();//文件md5
// if ($fileItem = File::where('md5', $md5)->find()) {
// $fileItem['updated_at'] = date('Y-m-d H:i:s');
//
// $fileItem->save();
// $res['location'] = $fileItem['src'];
// return json($res);
// }
if (!in_array(strtolower($image->getMime()), ['image/png','image/jpeg','image/jpg','image/gif'])) {
header("HTTP/1.1 400 MIME TYPE ERROR");
exit;
}
checkPathExistWithMake(public_path().$this->uploadPath.'/images/'.date('Ym'));
// tinymce富文本对图片进行操作后xuan上传的是blob文件。
// 针对blob文件未读取到后缀名 自动生成后缀 默认用mimetype后缀 如image/jpeg =》jpeg
$newFileName = $image->hashName('uniqid');
if (isset(explode('.',$newFileName)[1]) && empty(explode('.',$newFileName)[1])) {
$ext = explode('/', $image->getOriginalMime());
$newFileName .= $ext[1];
}
$src = Filesystem::putFileAs('images/'.date('Ym'), $image, $newFileName);
$src = $this->uploadPath.$this->DIRECTORY_SEPARATOR.$src;
if ($this->isCompress) {
// 剪切
//Image::resize($src);
}
File::add($image, $src, $md5); //加入上传文件表
$res['location'] = $src;
return json($res);
}
}
}

View File

@ -0,0 +1,306 @@
<?php
declare (strict_types=1);
namespace app\controller\manager;
use app\model\Account;
use app\model\Account as AccountModel;
use app\model\AccountWorksite;
use Exception;
use app\model\Log;
use think\Collection;
use think\response\View;
use think\response\Json;
use think\db\exception\DbException;
use think\exception\ValidateException;
use think\db\exception\DataNotFoundException;
use think\db\exception\ModelNotFoundException;
class Worksite extends Base
{
protected $noNeedLogin = ['getAccountList'];
/**
* 列表
*
* @throws Exception
*/
public function index()
{
if ($this->request->isPost()) {
$params = input('searchParams/a');
$page = input('page/d', 1);
$size = input('size/d', 20);
$where = [];
if (!empty($params)) {
foreach ($params as $key => $param) {
$param = trim($param);
if ($key == 'keyword') {
$where[] = ['w.name', 'like', '%'.$param.'%'];
continue;
}
if ($param == '0' || !empty($param)) {
$where[] = ['w.'.$key, 'like', '%'.$param.'%'];
}
}
}
$query = \app\model\Worksite::alias('w')->leftJoin('account a', 'a.id = w.manager_id')->where($where);
$total = $query->count();
$res = [
'total' => $total,
'current' => $page ?: 1,
'size' => $size ?: 20,
'list' => new Collection(),
];
if ($total > 0) {
$res['list'] = $query->fieldRaw('w.*,a.nickname,a.real_name,a.mobile')->page($page, $size)->order('w.sort', 'desc')->order('w.id', 'desc')->select();
$statusText = \app\model\Worksite::statusText();
$res['list']->each(function ($item) use ($statusText) {
$item->status_text = $statusText[$item->status] ?? '';
$item->manager = ($item->nickname ? '昵称:'.$item->nickname : '') .($item->real_name ? ' 姓名:'.$item->real_name : ''). ($item->mobile ? ' 手机:'.$item->mobile : '');
});
}
return $this->json(0, 'success', $res);
}
return $this->view();
}
/**
* 添加
*
* @return Json|View
*/
public function add()
{
if ($this->request->isPost()) {
try {
$input = input('post.');
if (!isset($input['name'])) {
return $this->json(4000, '参数错误');
}
$insert = [
'name' => $input['name'] ?? '',
'lng' => $input['lng'] ?? '',
'lat' => $input['lat'] ?? '',
'address' => $input['address'] ?? '',
'manager_id' => $input['manager_id'] ?? 0,
];
if ($input['manager_id'] > 0) {
if (AccountWorksite::where('account_id', $input['manager_id'])->count() > 0) {
return $this->json(4003, '该负责人已绑定其他工地');
}
}
$item = \app\model\Worksite::create($insert);
if ($input['manager_id'] > 0) {
AccountWorksite::where('worksite_id', $item->id)->delete();
AccountWorksite::create([
'worksite_id' => $item->id,
'account_id' => $input['manager_id'],
]);
Account::where('id', $input['manager_id'])->save(['role' => Account::ROLE_MANAGER]);
}
return $this->json();
} catch (Exception $e) {
return $this->json(4001, '添加失败'.$e->getMessage());
}
}
return $this->view();
}
/**
* 编辑
*
* @return \think\response\Json|\think\response\View
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function edit()
{
$id = input('id');
//通过ID查询
$item = \app\model\Worksite::where('id', (int) $id)->find();
if (empty($item)) {
return $this->json(4000, '没有相关记录!');
}
if ($this->request->isPost()) {
try {
$input = input('post.');
if (!isset($input['name']) || !isset($input['lng']) || !isset($input['lat'])) {
return $this->json(4000, '参数错误');
}
Account::where('id', $item['manager_id'])->save(['role' => Account::ROLE_NORMAL]);
AccountWorksite::where('worksite_id', $id)->delete();
if ($input['manager_id'] > 0) {
if (AccountWorksite::where('account_id', $input['manager_id'])->where('worksite_id', '<>', $id)->count() > 0) {
return $this->json(4003, '该负责人已绑定其他工地');
}
AccountWorksite::create([
'worksite_id' => $id,
'account_id' => $input['manager_id'],
]);
Account::where('id', $input['manager_id'])->save(['role' => Account::ROLE_MANAGER]);
}
$item->save([
'name' => $input['name'] ?? '',
'lng' => $input['lng'] ?? '',
'lat' => $input['lat'] ?? '',
'address' => $input['address'] ?? '',
'manager_id' => $input['manager_id'] ?? 0,
]);
return $this->json();
} catch (Exception $e) {
return $this->json(5000, $e->getMessage());
}
}
$bindAccount = Account::where('id', $item['manager_id'])->column('id,nickname,real_name,mobile');
$accountList = [];
foreach ($bindAccount as $ac) {
$accountList[] = [
'name_text' => $ac['nickname'].'【姓名:'.$ac['real_name'].'】【手机:'.$ac['mobile'].'】',
'id' => $ac['id'], 'selected' => true
];
}
$this->data['jsonStr'] = $bindAccount ? json_encode($accountList, JSON_UNESCAPED_UNICODE) : json_encode([]);
$this->data['item'] = $item;
$this->data['id'] = $id;
return $this->view();
}
/**
* 更新属性
*
* @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;
}
// 通过ID查询
if (!$info = \app\model\Worksite::where('id', (int) $item['id'])->find()) {
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 \think\response\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)) {
//删除逻辑
if (\app\model\Account::whereIn('Worksite', $ids)->count() > 0) {
return $this->json(4000, '所选岗位已分配给用户,请先移除后再删除');
}
\app\model\Worksite::whereIn('id', $ids)->delete();
Log::write(get_class(), 'del', '删除操作涉及到的ID为'.implode(',', $ids));
}
} catch (Exception $e) {
return $this->json(5000, $e->getMessage());
}
return $this->json();
}
/**
* 获取客户列表
*
* @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,302 @@
<?php
namespace app\controller\manager\account;
use app\controller\manager\Base;
use app\exception\RepositoryException;
use app\model\Account;
use app\model\Account as AccountModel;
use app\model\Position;
use app\model\Worksite;
use app\repository\AccountRepository;
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);
$orderNum = 0;
$orderScoreNum = 0;
$totalPrice = 0;
$totalScore = 0;
$item['total_price'] = Math::fen2Yuan($totalPrice);
$item['order_num'] = $orderNum;
$item['order_score_num'] = $orderScoreNum;
$item['order_newest'] = [];
$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, [
'real_name' => 'require',
'mobile' => '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 Redirect|Json|View
* @throws Exception
*/
public function worker()
{
$id = input('id/d', 0);
if (!$info = Account::find($id)) {
if ($this->request->isPost()) {
return $this->json(4000, '用户不存在');
} else {
return $this->error('用户不存在');
}
}
if ($this->request->isPost()) {
$item = input('post.');
$validate = $this->validateByApi($item, [
'real_name|姓名' => 'require',
'mobile|电话' => 'require',
'position|岗位' => 'require',
'pay|基本工资' => 'require',
'emergency_contact|紧急联系人' => 'require',
'emergency_phone|紧急联系人电话' => 'require',
'bank_card_name|银行卡户名' => 'require',
'bank_card_number|银行卡号' => 'require',
'bank_name|开户行' => 'require',
'card_number|身份证' => '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['positionList'] = Position::list();
$this->data['worksite_name'] = Worksite::where('id', $info['worksite_id'])->value('name');
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()
{
$position = Position::list();
if ($this->request->isPost()) {
$page = input('page/d', 1);
$size = input('size/d', 20);
$searchParams = input('searchParams');
$search = [];
if ($searchParams) {
foreach ($searchParams as $key => $param) {
if ($param || $param == '0') {
switch ($key) {
case 'keyword':
$search[] = ['nickname|real_name|mobile|emergency_contact|emergency_phone|bank_card_name|bank_card_number|bank_name|card_number', 'like', '%'.$param.'%'];
break;
case 'role':
switch ($param) {
case -1://全部
break;
case Account::ROLE_NORMAL:
case Account::ROLE_WORKER:
case Account::ROLE_MANAGER:
$search[] = ['role', '=', $param];
break;
case 3://单独构造的代号 员工 表示工人和负责人
$search[] = ['role', 'in', [Account::ROLE_WORKER, Account::ROLE_MANAGER]];
break;
}
break;
default:
$search[] = [$key, 'like', '%'.$param.'%'];
}
}
}
}
if (empty($searchParams['role'])) {
$search[] = ['role', 'in', [Account::ROLE_WORKER, Account::ROLE_MANAGER]];
}
try {
$items = Account::findList($search, [], $page, $size, function ($q) {
return $q->order('id', 'desc');
});
$worksite = Worksite::list();
$items['list']->each(function ($item) use ($position, $worksite) {
$genderText = Account::genderText()[$item->gender] ?? '保密';
$item->role_text = Account::roleText()[$item->role] ?? '其他';
$item->position_text = $position[$item->position] ?? '其他';
$item->worksite_name = $worksite[$item->position] ?? '';
$item->checking_text = Account::checkingText()[$item->checking] ?? '';
$item->gender_text = $genderText;
});
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['positionList'] = $position;
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();
}
}

18
app/event.php Normal file
View File

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

View File

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

View File

@ -0,0 +1,11 @@
<?php
namespace app\exception;
use Exception;
// 事件异常
class EventException extends Exception
{
}

View File

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

View File

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

View File

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

37
app/job/NotifySms.php Executable 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 Executable file
View File

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

35
app/middleware/ApiLogin.php Executable file
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 Executable 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 Executable 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 Executable 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 Executable 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 Executable 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);
}
}

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

@ -0,0 +1,92 @@
<?php
namespace app\model;
use think\model\relation\BelongsToMany;
use think\model\relation\HasMany;
class Account extends Base
{
public const ROLE_NORMAL = '0';//普通用户
public const ROLE_WORKER = '1';//工人
public const ROLE_MANAGER = '2';//负责人
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();
}
public static function roleText(): array
{
return [
self::ROLE_NORMAL => '普通用户',
self::ROLE_WORKER => '工人',
self::ROLE_MANAGER => '工地负责人',
];
}
public static function checkingText(): array
{
return [
0 => '正常',
1 => '资料审核中',
];
}
public static function genderText(): array
{
return [
self::GENDER_UNDEFINED => '保密',
self::GENDER_MALE => '男',
self::GENDER_FEMALE => '女',
];
}
// 获取用户信息 (包含待审核信息)
public static function getUser(int $accountId): array
{
$user = self::alias('a')
->leftJoin('position p', 'a.position = p.id')
->where('a.id', $accountId)
->field('a.*,p.name as position_name')
->find();
if (empty($user)) {
return [];
}
$user->headimgurl = resourceJoin($user->headimgurl, request()->domain());
$user['check_info'] = [];//待审核对比信息
if ($user->checking == self::COMMON_ON || $user->role == self::ROLE_WORKER) {
$user['check_info'] = CheckLog::getCheckInfo($accountId);
}
$user = $user->toArray();
unset($user['sex']);
unset($user['unionid']);
unset($user['last_login']);
unset($user['login_ip']);
unset($user['remarks']);
unset($user['remarks']);
return arrayNullToString($user);
}
// 工人-需要审核的字段
public static function needCheckFields(): array
{
return [
'real_name', 'mobile', 'position', 'pay', 'emergency_contact', 'emergency_phone', 'bank_card_name', 'bank_card_number',
'bank_name', 'card_number'
];
}
}

138
app/model/AccountCoupon.php Executable file
View File

@ -0,0 +1,138 @@
<?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];
}
/**
* 状态列表
*
* @return string[]
*/
public static function statusText(): 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 = ['id' => $id];
if (!$accountCoupon = self::findOne($where)) {
throw new RepositoryException('您的优惠券不存在');
}
if ($accountCoupon['account_id'] != $accountId) {
throw new RepositoryException('不是您的优惠券');
}
if (!empty($accountCoupon['deleted_at'])) {
throw new RepositoryException('您的优惠券不存在-2');
}
if (!$coupon = Coupon::findById($accountCoupon['coupon_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;
}
}

161
app/model/AccountDataLog.php Executable file
View File

@ -0,0 +1,161 @@
<?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';//分享注册-客户分享时客服获得积分
public const ACTION_SIGN = 'sign';//签到
/**
* 记录变更
*
* @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');
}
/**
* 来源用户
*
*/
public function sourceAccount(): HasOne
{
return $this->hasOne(Account::class, 'id', 'source_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 => '提现扣除',
];
}
//
/**
* 分销明显 根据身份返回不同
*
* @param int $accountId
* @param int $position 身份 0=普通用户 1=一级代理 2=二级代理
* @param array $options
* @return array
* @throws \Exception
*/
public static function salesList(int $accountId, int $position, array $options = []): array
{
$page = $options['page'] ?? 1;
$size = $options['size'] ?? 20;
$action = $options['action'] ?? '';//查询具体某类操作
$where = [];
$where[] = ['account_id', '=', $accountId];
$where[] = ['num', '>', 0];
if (!empty($action)) {
$where[] = ['action', '=', $action];
}
if ($position > 0) {
$where[] = ['type', '=', self::TYPE_COMMISSION];
} else {
$where[] = ['type', '=', self::TYPE_SCORE];
$where[] = ['action', '=', self::ACTION_ORDER];
}
$field = ['id', 'account_id', 'source_account_id', 'name', 'num', 'created_at', 'type'];
$list = self::findList($where, $field, $page, $size, function ($q) {
return $q->order('created_at', 'desc')->order('id', 'desc')->with([
'sourceAccount' => function ($query) {
$query->field('id,nickname,headimgurl');
}
]);
});
$list['list']->each(function ($item) {
$item->account = $item->sourceAccount ? json_decode($item->sourceAccount, true) : [];
if ($item['type'] == self::TYPE_SCORE) {
$item->num = (int) $item->num;
}
unset($item->sourceAccount);
});
return $list;
}
}

117
app/model/AccountRecord.php Executable file
View File

@ -0,0 +1,117 @@
<?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');
}
}

8
app/model/AccountSign.php Executable file
View File

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

13
app/model/AccountWorksite.php Executable file
View File

@ -0,0 +1,13 @@
<?php
namespace app\model;
/**
* 工地与负责人关联表
*
* Class AccountWorksite
* @package app\model
*/
class AccountWorksite extends Base
{
}

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

@ -0,0 +1,32 @@
<?php
namespace app\model;
/**
* 活动
* Class Activity
* @package app\model
*/
class Activity extends Base
{
// 是否有正在进行中的活动
public static function hasCurrent(): bool
{
$now = date('Y-m-d');
return self::where('is_current', 1)
->where('begin_at', '<=', $now)
->where('end_at', '>=', $now)
->count() > 0;
}
// 指定活动是否是正在进行中的活动
public static function isCurrent(int $id): bool
{
$now = date('Y-m-d');
return self::where('is_current', 1)
->where('id', $id)
->where('begin_at', '<=', $now)
->where('end_at', '>=', $now)
->count() > 0;
}
}

13
app/model/ActivityBusiness.php Executable file
View File

@ -0,0 +1,13 @@
<?php
namespace app\model;
/**
* 参与活动商家
* Class Activity
* @package app\model
*/
class ActivityBusiness extends Base
{
}

58
app/model/Archives.php Executable 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']);
}
}

86
app/model/ArchivesCategory.php Executable file
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']);
}
}

53
app/model/ArchivesModel.php Executable file
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();
}
}

118
app/model/ArchivesModelField.php Executable file
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 Executable 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 Executable 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 [];
}
}
}

29
app/model/CheckLog.php Normal file
View File

@ -0,0 +1,29 @@
<?php
namespace app\model;
/**
* 资料审核日志
*
* Class CheckLog
* @package app\model
*/
class CheckLog extends Base
{
// 获取用户待审核或审核失败的资料
public static function getCheckInfo(int $accountId)
{
return self::alias('cl')
->leftJoin('position p', 'p.id = cl.position')
->where('cl.account_id', $accountId)
// ->whereIn('cl.status', [self::COMMON_OFF, -1])
->field('cl.*,p.name as position_name')
->order('cl.id', 'desc')
->find();
}
public function account()
{
return $this->hasOne(Account::class, 'id', 'account_id');
}
}

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

@ -0,0 +1,41 @@
<?php
namespace app\model;
/**
* 打卡日志
*
* Class ClockLog
* @package app\model
*/
class ClockLog extends Base
{
public const TYPE_IN = 'in';//上班
public const TYPE_OUT = 'out';//下班
public const STATUS_TODO = 0;//待审核
public const STATUS_YES = 1;//通过
public const STATUS_NO = -1;//不通过
public function account()
{
return $this->hasOne(Account::class, 'id', 'account_id');
}
public static function typeText(): array
{
return [
self::TYPE_IN => '上班',
self::TYPE_OUT => '下班',
];
}
public static function statusText(): array
{
return [
self::STATUS_TODO => '待审核',
self::STATUS_YES => '审核通过',
self::STATUS_NO => '审核拒绝',
];
}
}

25
app/model/Config.php Executable file
View File

@ -0,0 +1,25 @@
<?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/shop-detail/shop-detail';
// 课程详情
public const MINI_PATH_COURSE_INFO = 'pagesB/course-detail/course-detail';
}

13
app/model/Coupon.php Executable file
View File

@ -0,0 +1,13 @@
<?php
namespace app\model;
/**
* 优惠券
* Class Coupon
* @package app\model
*/
class Coupon extends Base
{
}

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

@ -0,0 +1,167 @@
<?php
namespace app\model;
use app\service\AliOss;
use Exception;
use Qiniu\Auth;
use Qiniu\Storage\UploadManager;
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());
// }
// 七牛
try {
Config::load('extra/qiniu', 'qiniu');
$conf = config('qiniu');
// 控制台获取密钥https://portal.qiniu.com/user/key
$accessKey = $conf['qiniuAccessKey'] ?? '';
$secretKey = $conf['qiniuSecretKey'] ?? '';
$bucket = $conf['bucket'] ?? '';
// 构建鉴权对象
$auth = new Auth($accessKey, $secretKey);
// 生成上传 Token
$token = $auth->uploadToken($bucket);
// 要上传文件的本地路径
// $filePath = './php-logo.png';
$filePath = $realPath;
// 上传到七牛存储后保存的文件名
// $key = 'my-php-logo.png';
$key = ltrim($src, '/');
// 初始化 UploadManager 对象并进行文件的上传。
$uploadMgr = new UploadManager();
// 调用 UploadManager 的 putFile 方法进行文件的上传,该方法会判断文件大小,进而决定使用表单上传还是分片上传,无需手动配置。
list($ret, $err) = $uploadMgr->putFile($token, $key, $filePath);
if ($err !== null) {
\think\facade\Log::error('七牛云OSS上传文件失败 '.$err);
// var_dump($err);
} else {
// var_dump($ret);
}
} 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();
}
}

13
app/model/Goods.php Executable file
View File

@ -0,0 +1,13 @@
<?php
namespace app\model;
/**
* 商品
* Class Goods
* @package app\model
*/
class Goods extends Base
{
}

7
app/model/GoodsArea.php Executable file
View File

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

7
app/model/GoodsCategory.php Executable file
View File

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

41
app/model/Log.php Executable 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 Executable file
View File

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

26
app/model/Member.php Executable 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 Executable 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 Executable 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 Executable 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 Executable 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();
}
}

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

@ -0,0 +1,23 @@
<?php
namespace app\model;
use think\model\relation\HasOne;
/**
* 加班日志
*
* Class OvertimeLog
* @package app\model
*/
class OvertimeLog extends Base
{
public const STATUS_NORMAL = 0;//待审核
public const STATUS_YES = 1;//通过
public const STATUS_NO = -1;//拒绝
public function account(): HasOne
{
return $this->hasOne(Account::class, 'id', 'account_id');
}
}

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

@ -0,0 +1,69 @@
<?php
namespace app\model;
use think\model\relation\HasOne;
/**
* 薪资日志
* 以天为周期记录每个工人、工地的基本工资及加班工资情况
* 记录的创建,每一次打卡或加班申请就添加当天的记录(每天、每个工地、每个工人最多一条记录)
*
* !!!不要删除此表的任何数据!!!
* !!!不要删除此表的任何数据!!!
* !!!不要删除此表的任何数据!!!
*
* Class PayLog
* @package app\model
*/
class PayLog extends Base
{
public function account(): HasOne
{
return $this->hasOne(Account::class, 'id', 'account_id');
}
/**
* 创建日工资初始记录
*
* 若当日记录已存在则不创建
* 此时创建的记录 金额等数据都默认为0 需要等审核通过后在指定时间(如每日凌晨)统计相应金额并追加到此记录
*
* 使用场景举例每日凌晨2点定时任务 => 对审核通过的申请计算基本工资及加班工资追加到此记录
*
* 千万不要因为金额记录为0就把记录删除 后续的统计操作都默认使用update进行追加。若记录不存在则会出现统计不准确的严重后果。
* @param int $accountId 工人ID
* @param int $worksiteId 工地ID
* @param int $time 工作日期(打卡、加班) 格式为Ymd 20220925
* @return bool
* @throws \think\db\exception\DbException
*/
public static function createWhenNotExists(int $accountId, int $worksiteId, int $time): bool
{
$indexs = $accountId.'-'.$worksiteId.'-'.$time;
if (self::where('indexs', $indexs)->count() > 0) {
return true;
}
$year = date('Y', strtotime($time));
$month = date('m', strtotime($time));
$day = date('d', strtotime($time));
try {
self::create([
'account_id' => $accountId,
'worksite_id' => $worksiteId,
'time' => $time,
'indexs' => $indexs,
'year' => $year,
'month' => $month,
'day' => $day,
]);
} catch (\Exception $exception) {
//弱网时可能会出现并发提交忽略indexs唯一索引报错
\think\facade\Log::error('创建pay_log失败message:'.$exception->getMessage().' line:'.$exception->getLine());
}
return true;
}
}

20
app/model/PayMonthLog.php Normal file
View File

@ -0,0 +1,20 @@
<?php
namespace app\model;
use think\model\relation\HasOne;
/**
* 薪资按月统计 所有数据都基于pay_log计算而来
* 仅统计上月(含上月)以前
*
* Class PayMonthLog
* @package app\model
*/
class PayMonthLog extends Base
{
public function account(): HasOne
{
return $this->hasOne(Account::class, 'id', 'account_id');
}
}

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