代码功能更新

This commit is contained in:
root
2025-05-18 07:42:27 +00:00
parent 7fc1d422a1
commit 4a90c41da2
2083 changed files with 695218 additions and 3 deletions
Executable
+7
View File
@@ -0,0 +1,7 @@
/.idea
/.vscode
/vendor
/runtime
*.log
.env
composer.lock
Executable
+1
View File
@@ -0,0 +1 @@
Executable
+7
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>
Executable
+1
View File
@@ -0,0 +1 @@
Laytp遵循Apache2开源协议发布,并提供免费使用。
+115 -3
View File
@@ -1,3 +1,115 @@
# premium ## 目标
简单部署 迅速开展业务!五分钟安装 填写参数 即可!
会员开发
### 原理
我做好了 后端开通接口!你们负责开展业务即可!收钱到你们自己钱包!
## 前言
> 基于ThinkPHP6+LayUI开发
<div align="center">
</div>
<p align="center"><strong>价值源自分享 成功在于坚持</strong></p>
<p align="center">
<a href="https://t.me/xiaotouadmin" target="_blank">官方平台</a>
<a href="https://t.me/xiaotouadmin" rel="nofollow" >在线演示</a>
<a href="https://t.me/xiaotouadmin" target="_blank">框架文档</a>
</p>
<p align="center">
<font size="20" >一套基于tp6 开发已会员分销系统</font>
</p>
<p>交流①群:https://t.me/xiaotouadmin</p>
</b><br/>
<b>超管账号 admin 123456 </b><br/>
## 开发初衷
许多同学问 我也想开下下级怎么办!我没办法呀! 那就多开发一条分销系统吧!你们自己管理下级! 软件后续更新! 在安装目录下 运行 git pull 即可更新
## 框架优势
* `PHP框架选用简单且优美的ThinkPHP6`
* `参考官方文档,只需会PHP LayUI 开箱即用`
* `前后端完全分离,代码清晰明了`
* `封装常用Html表单元素,且仅需使用Html标记语言即可渲染表单元素`
* `基于Token验证的用户鉴权`
* `前端JavaScript鉴权,后端AUTH类鉴权,减少请求`
* `提供插件市场,方便框架丰富功能`
* `永久免费提供一键生成CURD插件,简单重复性CURD代码可以可视化一键生成`
* `永久免费提供一键生成Api文档插件,Api文档可以使用MEditor和代码注解生成`
* `代码安全质量高,修复大部分低危、高危代码漏洞`
## 集成功能
- [x] `API模块` token鉴权,签名规则
- [x] `权限管理` 后台权限管理
- [x] `管理员管理` 后台管理员管理
- [x] `角色管理` 后台管理员角色管理
- [x] `菜单管理` 后台菜单管理
- [x] `系统配置` 每个配置页面独立设置,仅需复制配置样例,修改html文件即可拥有多个配置界面
- [x] `插件市场` 可开发定制属于自己的插件,可安装升级社区插件,目前提供的插件:
- [x] `生成CRUD插件` 前后端代码的生成(php、html、layui、sql)支持一键生成CRUD
- [x] `生成Api文档插件` Api文档可以使用MEditor和代码注解生成
- [x] `UEditor编辑器插件``阿里云OSS对象存储插件``七牛云KODO对象存储插件`深度兼容,编辑器的上传也可以上传到`阿里云``七牛云`
- [x] `MEditor编辑器插件``阿里云OSS对象存储插件``七牛云KODO对象存储插件`深度兼容,编辑器的上传也可以上传到`阿里云``七牛云`
- [x] `阿里云OSS对象存储插件`
- [x] `七牛云KODO对象存储插件`
- [x] `阿里云短信插件`
- [x] `Email邮件服务插件`
- [x] `操作日志` 用户后台操作日志,Api接口请求日志,后台管理员登录日志
- [x] `地区管理`
- [x] `附件管理`
## 常用表单元素封装
对于常用表单元素,在`Laytp`中,无需写太多JS代码,你只需要像如下这样
```
<div class="表单元素class标识"
data-属性名="属性值"
>
</div>
```
就可以渲染常用表单元素,比如上传组件,多选下拉框组件,编辑器,省市区联动下拉框等
## 安装使用
1、首先将本框架直接clone到你本地,或者直接下载
```
git clone https://gitee.com/junstar/laytp
```
2、先设置mysql的sql_mode=NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION 然后创建一个数据库
```
数据库字符集 utf8mb4 -- UTF-8 Unicode
排序规则 utf8mb4_general_ci
```
3、复制根目录下的.example.env文件成.env文件,修改.env文件的数据库连接部分
```
[DATABASE]
TYPE = mysql
HOSTNAME = 127.0.0.1
DATABASE = 创建的数据库名称
USERNAME = 数据库用户名
PASSWORD = 数据库密码
HOSTPORT = 3306
CHARSET = utf8
DEBUG = true
PREFIX = 数据库前缀,推荐使用lt_
```
4、根目录执行 composer install 安装必要的Composer包,包括ThinkPHP6框架和ThinkPHP6的其他代码!
```
composer install
```
5、根目录执行ThinkPHP6的数据库迁移命令,这里会导入Laytp框架需要的数据库文件
```
php think migrate:run
```
Executable
+10
View File
File diff suppressed because one or more lines are too long
+46
View File
@@ -0,0 +1,46 @@
<?php
namespace app\controller;
use think\facade\Db;
use laytp\BaseController;
use think\facade\Cache;
class Index extends BaseController
{
public function index()
{
return redirect("/admin/index.html");
}
public function getnumber()
{
$uplist=Cache::get("getnumber");
if(empty($uplist)){
$url="http://git.zf789.life/kaifa/premium/graph";
$html= file_get_contents($url);
preg_match_all('/<span class="message tw-inline-block gt-ellipsis tw-mr-2">\s*<span>(.*?)<\/span>\s*<\/span>/', $html, $matches);
$uplist=array();
if ($matches[1]) {
$messages = $matches[1]; // Array of all matched messages
foreach ($messages as $message) {
$uplist[]=$message;
// var_dump($message);
// Process each message
}
}
Cache::set("getnumber",$uplist,1024);
}
//平台总用户
$ptzuser=Db::name("member")->count();
//平台托管机器人
$ptzbot=Db::name("admin_bot")->count();
//平台机器人总余额
$ptzbotmoney=Db::name("admin_bot")->sum('money');
//平台总利润
$ptlirun=Db::name("order")->sum('lirun');
return $this->success('返回成功', ['uplist'=>$uplist,'ptzuser'=>$ptzuser,'ptzbot'=>$ptzbot,'ptzbotmoney'=>$ptzbotmoney,'ptlirun'=>$ptlirun]);
}
}
+109
View File
@@ -0,0 +1,109 @@
<?php
namespace app\controller\admin;
use laytp\controller\Backend;
use laytp\library\CommonFun;
use laytp\library\Tree;
use think\facade\Config;
/**
* 地区管理
*/
class Area extends Backend
{
/**
* area模型对象
* @var \app\model\Area
*/
protected $model;
public $hasSoftDel = 1;//是否拥有软删除功能
public $orderRule = ['sort' => 'DESC', 'id' => 'ASC'];
public function _initialize()
{
$this->model = new \app\model\Area();
}
//查看
public function index()
{
$where = $this->buildSearchParams();
$order = $this->buildOrder();
$sourceData = $this->model->where($where)->order($order)->with(['parent']);
$isTree = $this->request->param('is_tree');
if ($isTree) {
$menuTreeObj = Tree::instance();
$menuTreeObj->init($sourceData->select()->toArray());
$data = $menuTreeObj->getRootTrees();
} else {
$paging = $this->request->param('paging', false);
if ($paging) {
$limit = $this->request->param('limit', Config::get('paginate.limit'));
$data = $sourceData->paginate($limit)->toArray();
$data['data'] = $this->getSelectedData($data['data']);
} else {
$data = $sourceData->select()->toArray();
}
}
return $this->success('数据获取成功', $data);
}
//删除
public function del()
{
$ids = $this->request->post('ids');
if (!$ids) {
return $this->error('参数ids不能为空');
}
$sourceData = $this->model->select()->toArray();
$treeLib = Tree::instance();
$treeLib->init($sourceData);
$childIds = $treeLib->getChildIds($ids);
if ($this->model->destroy($childIds)) {
return $this->success('数据删除成功');
} else {
return $this->error('数据删除失败');
}
}
//回收站
public function recycle()
{
$where = $this->buildSearchParams();
$order = $this->buildOrder();
$limit = $this->request->param('limit', Config::get('paginate.limit'));
$data = $this->model->onlyTrashed()
->with(['parent'])
->order($order)->where($where)->paginate($limit)->toArray();
return $this->success('回收站数据获取成功', $data);
}
//设置排序
public function setSort()
{
$id = $this->request->post('id');
$fieldVal = $this->request->post('field_val');
$isRecycle = $this->request->post('is_recycle');
$update['sort'] = $fieldVal;
try {
if ($isRecycle) {
$updateRes = $this->model->onlyTrashed()->where('id', '=', $id)->update($update);
} else {
$updateRes = $this->model->where('id', '=', $id)->update($update);
}
if ($updateRes) {
return $this->success('操作成功');
} else if ($updateRes === 0) {
return $this->success('未作修改');
} else {
return $this->error('操作失败');
}
} catch (\Exception $e) {
return $this->error('数据库异常,操作失败');
}
}
}
+10
View File
File diff suppressed because one or more lines are too long
+235
View File
@@ -0,0 +1,235 @@
<?php
namespace app\controller\admin;
use app\service\admin\UserServiceFacade;
use app\service\ConfServiceFacade;
use laytp\library\UploadDomain;
use plugin\ali_oss\service\Oss;
use plugin\qiniu_kodo\service\Kodo;
use laytp\controller\Backend;
use think\facade\Env;
use think\facade\Filesystem;
use think\File;
class Common extends Backend
{
protected $noNeedAuth = ['*'];
protected $noNeedLogin = ['getLoginNeedCaptchaConf'];
/**
* 获取登录后台是否需要验证码的配置
* 这个接口是后台登录界面使用的,给这个接口独立的访问权限,无需登录,无需鉴权
*/
public function getLoginNeedCaptchaConf()
{
return $this->success('获取成功', ConfServiceFacade::get('system.basic.loginNeedCaptcha', 0));
}
//上传接口
public function upload()
{
try {
$defaultType = ConfServiceFacade::get('system.upload.defaultType', 'local');
$uploadType = $this->request->param('upload_type', 'default');
if ($uploadType == 'default') $uploadType = $defaultType;
if (!in_array($uploadType, ['local', 'ali-oss', 'qiniu-kodo'])) {
return $this->error($uploadType . '上传方式未定义');
}
$file = $this->request->file('laytpUploadFile'); // 获取上传的文件
if (!$file) {
return $this->error('上传失败,请选择需要上传的文件');
}
$fileExt = strtolower($file->getOriginalExtension());
$uploadDomain = new UploadDomain();
if (!$uploadDomain->check($file->getOriginalName(), $file->getSize(), $fileExt, $file->getMime())) {
return $this->error($uploadDomain->getError());
}
$saveName = date("Ymd") . "/" . md5(uniqid(mt_rand())) . ".{$fileExt}";
/**
* 不能以斜杆开头
* - 因为OSS存储时,不允许以/开头
*/
$uploadDir = $this->request->param('dir');
$object = $uploadDir ? $uploadDir . '/' . $saveName : $saveName;//设置了上传目录的上传文件名
$filePath = $object; //保存到lt_files中的path
//如果上传的是图片,验证图片的宽和高
$accept = $this->request->param('accept');
if ($accept == "image") {
$width = $this->request->param('width');
$height = $this->request->param('height');
if ($width || $height) {
$imageInfo = getimagesize($file->getFileInfo());
if (($width && $imageInfo[0] > $width) || ($height && $imageInfo[1] > $height)) {
return $this->error('上传失败,图片尺寸要求宽:' . $width . 'px,高:' . $height . 'px,实际上传文件[ ' . $file->getOriginalName() . ' ]的尺寸为宽' . $imageInfo[0] . 'px,高:' . $imageInfo[1] . 'px');
}
}
}
$inputValue = "";
//上传至七牛云
if ($uploadType == 'qiniu-kodo') {
if (ConfServiceFacade::get('plugin.qiniu_kodo.switch') != 1) {
return $this->error('未开启七牛云KODO存储,请到七牛云KODO配置中开启,如果未安装七牛云KODO存储插件,请先到插件市场进行安装');
}
$kodoConf = [
'accessKey' => ConfServiceFacade::get('plugin.qiniu_kodo.accessKey'),
'secretKey' => ConfServiceFacade::get('plugin.qiniu_kodo.secretKey'),
'bucket' => ConfServiceFacade::get('plugin.qiniu_kodo.bucket'),
'domain' => ConfServiceFacade::get('plugin.qiniu_kodo.domain'),
];
$kodo = Kodo::instance();
$kodoRes = $kodo->upload($file->getPathname(), $object, $kodoConf);
if ($kodoRes) {
$inputValue = $kodoRes;
} else {
return $this->error($kodo->getError());
}
}
//上传至阿里云
if ($uploadType == 'ali-oss') {
if (ConfServiceFacade::get('plugin.ali_oss.switch') != 1) {
return $this->error('未开启阿里云OSS存储,请到阿里云OSS配置中开启');
}
$ossConf = [
'accessKeyID' => ConfServiceFacade::get('plugin.ali_oss.accessKeyID'),
'accessKeySecret' => ConfServiceFacade::get('plugin.ali_oss.accessKeySecret'),
'bucket' => ConfServiceFacade::get('plugin.ali_oss.bucket'),
'endpoint' => ConfServiceFacade::get('plugin.ali_oss.endpoint'),
'domain' => ConfServiceFacade::get('plugin.ali_oss.domain'),
];
$oss = Oss::instance();
$ossUploadRes = $oss->upload($file->getPathname(), $object, $ossConf);
if ($ossUploadRes) {
$inputValue = $ossUploadRes;
} else {
return $this->error($oss->getError());
}
}
//本地上传
if ($uploadType == 'local') {
$uploadDir = ltrim('/', $uploadDir);
$saveName = Filesystem::putFileAs('/' . $uploadDir, $file, '/' . $object);
$filePath = $saveName;
$staticDomain = Env::get('domain.static');
if ($staticDomain) {
$inputValue = $staticDomain . '/storage/' . $saveName;
} else {
$inputValue = request()->domain() . '/static/storage/' . $saveName;
}
}
//将inputValue存入lt_files表中
$filesModel = new \app\model\Files();
$fileId = $filesModel->insertGetId([
'category_id' => 0,
'name' => $file->getOriginalName(),
'file_type' => $this->request->param('accept'),
'path' => $filePath,
'upload_type' => $uploadType,
'size' => $file->getSize(),
'ext' => $file->getExtension(),
'create_admin_user_id' => UserServiceFacade::getUser()->id,
'update_admin_user_id' => UserServiceFacade::getUser()->id,
'create_time' => date('Y-m-d H:i:s'),
'update_time' => date('Y-m-d H:i:s'),
]);
return $this->success('上传成功', [
'id' => $fileId,
'path' => $inputValue,
'name' => $file->getOriginalName(),
]);
} catch (\Exception $e) {
return $this->exceptionError($e);
}
}
// 获取阿里云sts的临时凭证,目前仅用于客户端直接上传到oss前获取到临时凭证进行上传
public function aliSts()
{
if (ConfServiceFacade::get('plugin.ali_oss_sts.switch') != 1) {
return $this->error('阿里云OSS存储的STS方式,请到阿里云STS配置中开启');
}
$uploadDomain = new UploadDomain();
$fileName = $this->request->param('name');
$fileSize = $this->request->param('size');
$fileExt = $this->request->param('ext');
if (!$uploadDomain->check($fileName, $fileSize, $fileExt, '')) {
return $this->error($uploadDomain->getError());
}
$stsConf = [
'accessKeyID' => ConfServiceFacade::get('plugin.ali_oss_sts.accessKeyID'),
'accessKeySecret' => ConfServiceFacade::get('plugin.ali_oss_sts.accessKeySecret'),
'ARN' => ConfServiceFacade::get('plugin.ali_oss_sts.ARN'),
'endpoint' => ConfServiceFacade::get('plugin.ali_oss_sts.endpoint'),
'domain' => ConfServiceFacade::get('plugin.ali_oss_sts.domain'),
'bucket' => ConfServiceFacade::get('plugin.ali_oss_sts.bucket'),
];
$oss = Oss::instance();
$sts = $oss->sts($stsConf);
$file = new File('', false);
if ($sts) {
return $this->success('sts获取成功', [
'sts' => $sts,
'path' => str_replace('\\', '/', $file->hashName()) . $fileExt,
'index' => $this->request->param('index'),
]);
} else {
return $this->error('sts获取失败,' . $oss->getError());
}
}
// 获取kodo上传凭证token
public function kodoToken()
{
if (ConfServiceFacade::get('plugin.qiniu_kodo.switch') != 1) {
return $this->error('未开启七牛云KODO存储,请到七牛云KODO配置中开启');
}
$uploadDomain = new UploadDomain();
$fileName = $this->request->param('name');
$fileSize = $this->request->param('size');
$fileExt = $this->request->param('ext');
if (!$uploadDomain->check($fileName, $fileSize, $fileExt, '')) {
return $this->error($uploadDomain->getError());
}
$kodoConf = [
'accessKey' => ConfServiceFacade::get('plugin.qiniu_kodo.accessKey'),
'secretKey' => ConfServiceFacade::get('plugin.qiniu_kodo.secretKey'),
'domain' => ConfServiceFacade::get('plugin.qiniu_kodo.domain'),
'bucket' => ConfServiceFacade::get('plugin.qiniu_kodo.bucket'),
];
$kodo = Kodo::instance();
$token = $kodo->token($kodoConf);
$file = new File('', false);
if ($token) {
return $this->success('KodoToken获取成功', [
'token' => $token,
'domain' => ConfServiceFacade::get('plugin.qiniu_kodo.domain'),
'bucket' => ConfServiceFacade::get('plugin.qiniu_kodo.bucket'),
'path' => str_replace('\\', '/', $file->hashName()) . $fileExt,
'index' => $this->request->param('index'),
]);
} else {
return $this->error('KodoToken获取失败,' . $kodo->getError());
}
}
//解锁屏幕
function unLockScreen()
{
$password = $this->request->post('password');
$userId = UserServiceFacade::getUser()->id;
$passwordHash = User::where('id', '=', $userId)->value('password');
if (!password_verify(md5($password), $passwordHash)) {
return $this->error('解锁失败,密码错误');
} else {
return $this->success('解锁成功');
}
}
}
+81
View File
@@ -0,0 +1,81 @@
<?php
namespace app\controller\admin;
use app\service\ConfServiceFacade;
use laytp\controller\Backend;
use laytp\library\CommonFun;
use laytp\library\UploadDomain;
use think\facade\Db;
/**
* 系统配置控制器
* Class Conf
* @package app\admin\controller
*/
class Conf extends Backend
{
protected $model;
protected $noNeedAuth = ['getGroupConf', 'saveGroupConf'];
public function _initialize()
{
$this->model = new \app\model\Conf();
}
/**
* 获取某个分组下所有的配置项
*/
public function getGroupConf()
{
$group = $this->request->param('group');
$return = ConfServiceFacade::groupGet($group, true);
return $this->success('获取成功', $return);
}
/**
* 保存配置
*/
public function saveGroupConf()
{
$post = $this->request->post();
if (isset($post['laytpUploadFile'])) {
unset($post['laytpUploadFile']);
}
$group = $post['group'];
unset($post['group']);
$formType = $post['form_type'];
unset($post['form_type']);
$allConf = [];
foreach ($post as $key => $value) {
if (is_array($value)) {
$temp = [];
foreach ($value['key'] as $arrK => $arrV) {
if ($arrV) {
$temp[$arrV] = $value['value'][$arrK];
}
}
$value = $temp;
}
$conf['group'] = $group;
$conf['key'] = $key;
$conf['value'] = $value;
$conf['form_type'] = $formType[$key];
$allConf[] = $conf;
}
ConfServiceFacade::groupSet($allConf);
return $this->success('保存成功', $allConf);
}
/**
* 删除某个分组下某个key的配置信息
* 这个不在配置页面进行调用,后续要做生成工具的时候统一管理所有的key
*/
public function del()
{
$group = $this->request->param('group');
$key = $this->request->param('key');
ConfServiceFacade::del($group, $key);
return $this->success('删除成功');
}
}
+135
View File
@@ -0,0 +1,135 @@
<?php
namespace app\controller\admin;
use laytp\controller\Backend;
use app\service\admin\UserServiceFacade;
use laytp\library\CommonFun;
use laytp\library\UploadDomain;
use think\facade\Config;
/**
* 附件管理
*/
class Files extends Backend
{
/**
* files模型对象
* @var \app\model\Files
*/
protected $model;
public $hasSoftDel = 1;//是否拥有软删除功能
public function initialize()
{
parent::initialize();
$this->model = new \app\model\Files();
}
/**
* 查看
* @throws \think\db\exception\DbException
*/
public function index()
{
$where = $this->buildSearchParams();
$order = $this->buildOrder();
$data = $this->model->where($where)->order($order)->with(['category', 'createAdminUser'=>['avatarFile']]);
$paging = $this->request->param('paging', false);
if ($paging) {
$limit = $this->request->param('limit', Config::get('paginate.limit'));
$data = $data->paginate($limit)->toArray();
$data['data'] = $this->getSelectedData($data['data']);
} else {
$data = $data->select()->toArray();
}
return $this->success('数据获取成功', $data);
}
//添加
public function add()
{
return $this->error('当前版本暂时不支持在附件管理中添加附件');
$post = CommonFun::filterPostData($this->request->post());
$post['create_admin_user_id'] = UserServiceFacade::getUser()->id;
$post['update_admin_user_id'] = UserServiceFacade::getUser()->id;
$post['path'] = UploadDomain::singleDelUploadDomain($post['path']);
if ($this->model->create($post)) {
return $this->success('添加成功', $post);
} else {
return $this->error('操作失败');
}
}
//不经过服务器方式上传成功后,回调的ajax请求地址,将上传成功的文件信息存入表中
public function unViaSave()
{
$post = $this->request->post();
$post['create_admin_user_id'] = UserServiceFacade::getUser()->id;
$post['update_admin_user_id'] = UserServiceFacade::getUser()->id;
$post['create_time'] = date('Y-m-d H:i:s');
$post['update_time'] = date('Y-m-d H:i:s');
$post['id'] = $this->model->insertGetId($post);
if ($post['id']) {
return $this->success('添加成功', $post);
} else {
return $this->error('操作失败');
}
}
//编辑
public function edit()
{
return $this->error('当前版本暂时不支持在附件管理中编辑附件');
$id = $this->request->param('id');
$info = $this->model->find($id);
$post = CommonFun::filterPostData($this->request->post());
$post['update_admin_user_id'] = UserServiceFacade::getUser()->id;
$post['path'] = UploadDomain::singleDelUploadDomain($post['path']);
foreach ($post as $k => $v) {
$info->$k = $v;
}
try {
$updateRes = $info->save();
if ($updateRes) {
return $this->success('编辑成功');
} else if ($updateRes === 0) {
return $this->success('未做修改');
} else if ($updateRes === null) {
return $this->error('操作失败');
}
} catch (\Exception $e) {
return $this->exceptionError($e);
}
}
//删除
public function del()
{
$ids = array_filter($this->request->post('ids'));
if (!$ids) {
return $this->error('参数ids不能为空');
}
try {
if ($this->model->destroy($ids)) {
return $this->success('数据删除成功');
} else {
return $this->error('数据删除失败');
}
} catch (\Exception $e) {
return $this->exceptionError($e);
}
}
//回收站
public function recycle()
{
$where = $this->buildSearchParams();
$order = $this->buildOrder();
$limit = $this->request->param('limit', Config::get('paginate.limit'));
$data = $this->model->onlyTrashed()
->with(['category', 'createAdminUser', 'updateAdminUser'])
->order($order)->where($where)->paginate($limit)->toArray();
return $this->success('回收站数据获取成功', $data);
}
}
+138
View File
@@ -0,0 +1,138 @@
<?php
namespace app\controller\admin;
use laytp\controller\Backend;
use think\facade\Config;
use laytp\library\CommonFun;
use app\validate\admin\member\Add;
use app\validate\admin\member\Edit;
use laytp\library\Str;
use laytp\library\UploadDomain;
/**
* 会员管理
*/
class Member extends Backend
{
/**
* member模型对象
* @var \app\model\Member
*/
protected $model;
protected $hasSoftDel=1;//是否拥有软删除功能
protected $noNeedLogin = []; // 无需登录即可请求的方法
protected $noNeedAuth = ['index', 'info']; // 无需鉴权即可请求的方法
public function _initialize()
{
$this->model = new \app\model\Member();
}
//查看和搜索列表
public function index(){
$where = $this->buildSearchParams();
$order = $this->buildOrder();
$data = $this->model->where($where)->order($order)->with(['avatar_pic_file']);
$paging = $this->request->param('paging', false);
if ($paging) {
$limit = $this->request->param('limit', Config::get('paginate.limit'));
$data = $data->paginate($limit)->toArray();
$data['data'] = $this->getSelectedData($data['data']);
} else {
$data = $data->select()->toArray();
}
return $this->success('数据获取成功', $data);
}
//添加
public function add()
{
$post = CommonFun::filterPostData($this->request->post());
$validate = new Add();
if(!$validate->check($post)){
return $this->error($validate->getError());
}
if(isset($post['password']) && $post['password']) $post['password'] = Str::createPassword($post['password']);
try {
if ($this->model->create($post)) {
return $this->success('添加成功', $post);
} else {
return $this->error('操作失败');
}
} catch (\Exception $e) {
return $this->exceptionError($e);
}
}
//查看详情
public function info()
{
$id = $this->request->param('id');
$info = $this->model->with(['avatar_pic_file'])->find($id);
return $this->success('获取成功', $info);
}
//编辑
public function edit(){
$id = $this->request->param('id');
$info = $this->model->find($id);
$post = CommonFun::filterPostData($this->request->post());
$validate = new Edit();
if(!$validate->check($post)){
return $this->error($validate->getError());
}
if(!$post['password']){
unset($post['password']);
}else{
$post['password'] = Str::createPassword($post['password']);
}
foreach ($post as $k => $v) {
$info->$k = $v;
}
try {
$updateRes = $info->save();
if ($updateRes) {
return $this->success('编辑成功');
} else {
return $this->error('操作失败');
}
} catch (\Exception $e) {
return $this->exceptionError($e);
}
}
//设置账号状态
public function setStatus()
{
$id = $this->request->post('id');
$fieldVal = $this->request->post('field_val');
$isRecycle = $this->request->post('is_recycle');
$update['status'] = $fieldVal;
try {
if($isRecycle) {
$updateRes = $this->model->onlyTrashed()->where('id', '=', $id)->update($update);
} else {
$updateRes = $this->model->where('id', '=', $id)->update($update);
}
if ($updateRes) {
return $this->success('操作成功');
} else if ($updateRes === 0) {
return $this->success('未作修改');
} else {
return $this->error('操作失败');
}
} catch (\Exception $e) {
return $this->error('数据库异常,操作失败');
}
}
}
+267
View File
@@ -0,0 +1,267 @@
<?php
namespace app\controller\admin;
use app\service\admin\UserServiceFacade;
use laytp\controller\Backend;
use laytp\library\CommonFun;
use laytp\library\Tree;
use think\facade\Config;
/**
* 菜单控制器
*/
class Menu extends Backend
{
public $noNeedAuth = ['getMenuTree', 'getTree'];
public $model;
public $orderRule = ['sort' => 'desc', 'id' => 'asc'];
public function _initialize()
{
$this->model = new \app\model\admin\Menu();
}
public function index()
{
$where = $this->buildSearchParams();
$order = $this->buildOrder();
$sourceData = $this->model->where($where)->order($order);
$isTree = $this->request->param('is_tree');
if ($isTree) {
$menuTreeObj = Tree::instance();
$menuTreeObj->init($sourceData->select()->toArray());
$data = $menuTreeObj->getRootTrees();
} else {
$paging = $this->request->param('paging', false);
if ($paging) {
$limit = $this->request->param('limit', Config::get('paginate.limit'));
$data = $sourceData->paginate($limit)->toArray();
$data['data'] = $this->getSelectedData($data['data']);
} else {
$data = $sourceData->select()->toArray();
}
}
return $this->success('数据获取成功', $data);
}
//获取当前登录者的权限列表,返回树形数据,角色管理赋予权限时用到
public function getTree()
{
$user = UserServiceFacade::getUser();
if ($user->is_super_manager === 1) {
$sourceData = $this->model->order($this->orderRule)->select()->toArray();
} else {
$roleIds = \app\model\admin\role\User::where('admin_user_id', '=', $user->id)
->column('admin_role_id');
$menuIds = \app\model\admin\menu\Role::where('admin_role_id', 'in', $roleIds)
->column('admin_menu_id');
$where[] = ['id', 'in', $menuIds];
$sourceData = $this->model->order($this->orderRule)->where($where)->select()->toArray();
}
$menuTreeObj = Tree::instance();
$menuTreeObj->init($sourceData);
//由列表数据转化成树形结构数据
$data = $menuTreeObj->getRootTrees();
return $this->success('获取成功', $data);
}
//获取当前登录者的菜单列表,返回树形数据,仅返回is_menu=1的列表,后台菜单列表展示使用
public function getMenuTree()
{
$user = UserServiceFacade::getUser();
$where[] = ['is_show', '=', 1];
$where[] = ['is_menu', '=', 1];
if ($user->is_super_manager === 1) {
$sourceData = $this->model->order($this->orderRule)->where($where)->select()->toArray();
} else {
$roleIds = \app\model\admin\role\User::where('admin_user_id', '=', $user->id)
->column('admin_role_id');
$menuIds = \app\model\admin\menu\Role::where('admin_role_id', 'in', $roleIds)
->column('admin_menu_id');
$where[] = ['id', 'in', $menuIds];
$sourceData = $this->model->order($this->orderRule)->where($where)->select()->toArray();
}
$menuTreeObj = Tree::instance();
$menuTreeObj->init($sourceData);
//由列表数据转化成树形结构数据
$data = $menuTreeObj->getRootTrees();
return $this->success('获取成功', $data);
}
//添加
public function add()
{
$post = CommonFun::filterPostData($this->request->post());
if ($post['rule'] && substr($post['rule'], 0, 1) != '/') {
$post['rule'] = '/' . $post['rule'];
}
if ($this->model->create($post)) {
return $this->success('添加成功', $post);
} else {
return $this->error('操作失败');
}
}
//编辑
public function edit()
{
$id = $this->request->param('id');
$info = $this->model->find($id);
$post = CommonFun::filterPostData($this->request->post());
if ($post['rule'] && substr($post['rule'], 0, 1) != '/') {
$post['rule'] = '/' . $post['rule'];
}
if ($id == $post['pid']) {
return $this->error('不能将上级改成自己');
}
foreach ($post as $k => $v) {
$info->$k = $v;
}
$update_res = $info->save();
if ($update_res) {
return $this->success('操作成功');
} else if ($update_res === 0) {
return $this->success('未做修改');
} else {
return $this->error('操作失败');
}
}
//删除
public function del()
{
$ids = $this->request->post('ids');
if (!$ids) {
return $this->error('参数ids不能为空');
}
$sourceData = $this->model->select()->toArray();
$treeLib = Tree::instance();
$treeLib->init($sourceData);
$childIds = $treeLib->getChildIds($ids);
if ($this->model->destroy($childIds)) {
return $this->success('数据删除成功', $childIds);
} else {
return $this->error('数据删除失败');
}
}
//设置排序
public function setSort()
{
$id = $this->request->post('id');
$fieldVal = $this->request->post('field_val');
$isRecycle = $this->request->post('is_recycle');
$update['sort'] = $fieldVal;
try {
if ($isRecycle) {
$updateRes = $this->model->onlyTrashed()->where('id', '=', $id)->update($update);
} else {
$updateRes = $this->model->where('id', '=', $id)->update($update);
}
if ($updateRes) {
return $this->success('操作成功');
} else if ($updateRes === 0) {
return $this->success('未作修改');
} else {
return $this->error('操作失败');
}
} catch (\Exception $e) {
return $this->exceptionError($e);
}
}
//设置是否为菜单
public function setIsMenu()
{
$id = $this->request->post('id');
$fieldVal = $this->request->post('field_val');
$isRecycle = $this->request->post('is_recycle');
$update['is_menu'] = $fieldVal;
try {
if ($isRecycle) {
$updateRes = $this->model->onlyTrashed()->where('id', '=', $id)->update($update);
} else {
$updateRes = $this->model->where('id', '=', $id)->update($update);
}
if ($updateRes) {
return $this->success('操作成功');
} else if ($updateRes === 0) {
return $this->success('未作修改');
} else {
return $this->error('操作失败');
}
} catch (\Exception $e) {
return $this->exceptionError($e);
}
}
//设置是否显示
public function setIsShow()
{
$id = $this->request->post('id');
$fieldVal = $this->request->post('field_val');
$isRecycle = $this->request->post('is_recycle');
$update['is_show'] = $fieldVal;
try {
if ($isRecycle) {
$updateRes = $this->model->onlyTrashed()->where('id', '=', $id)->update($update);
} else {
$updateRes = $this->model->where('id', '=', $id)->update($update);
}
if ($updateRes) {
return $this->success('操作成功');
} else if ($updateRes === 0) {
return $this->success('未作修改');
} else {
return $this->error('操作失败');
}
} catch (\Exception $e) {
return $this->exceptionError($e);
}
}
// 复制菜单
public function copy()
{
$pid = (int)$this->request->post('pid');
$ids = $this->request->post('ids');
if (!$ids) {
return $this->error('参数ids不能为空');
}
$data = \app\model\admin\Menu::where('id', 'in', $ids)
->withoutField('id')->select()
->each(function ($item) use ($pid) {
$item->pid = $pid;
})->toArray();
$insert = $this->model->insertAll($data);
if ($insert) {
return $this->success('复制成功');
} else {
return $this->error('复制失败');
}
}
// 移动菜单
public function move()
{
$pid = (int)$this->request->post('pid');
$ids = $this->request->post('ids');
if (!$ids) {
return $this->error('参数ids不能为空');
}
$save = \app\model\admin\Menu::where('id', 'in', $ids)
->save(['pid' => $pid]);
if ($save) {
return $this->success('移动成功');
} else {
return $this->error('移动失败');
}
}
}
+158
View File
@@ -0,0 +1,158 @@
<?php
namespace app\controller\admin;
use laytp\controller\Backend;
use think\facade\Config;
use laytp\library\CommonFun;
use app\validate\admin\bot\Add;
use app\validate\admin\bot\Edit;
use laytp\library\Str;
use laytp\library\UploadDomain;
use app\service\Telegram as Telegrambot;
use think\facade\Request;
/**
* 会员管理
*/
class Order extends Backend
{
/**
* member模型对象
* @var \app\model\Bot
*/
protected $model;
protected $hasSoftDel=1;//是否拥有软删除功能
protected $noNeedLogin = []; // 无需登录即可请求的方法
protected $noNeedAuth = ['index', 'info']; // 无需鉴权即可请求的方法
public function _initialize()
{
$this->model = new \app\model\Order();
}
//查看和搜索列表
public function index(){
$where = $this->buildSearchParams();
$order = $this->buildOrder();
//var_dump($where,$order);
$data = $this->model->where($where)->order($order)->with(['avatar_pic_file']);
$paging = $this->request->param('paging', false);
if ($paging) {
$limit = $this->request->param('limit', Config::get('paginate.limit'));
$data = $data->paginate($limit)->toArray();
$data['data'] = $this->getSelectedData($data['data']);
} else {
$data = $data->select()->toArray();
}
return $this->success('数据获取成功', $data);
}
//添加
public function add()
{
$post = CommonFun::filterPostData($this->request->post());
$validate = new Add();
if(!$validate->check($post)){
return $this->error($validate->getError());
}
try {
if ($this->model->create($post)) {
return $this->success('添加成功', $post);
} else {
return $this->error('操作失败');
}
} catch (\Exception $e) {
return $this->exceptionError($e);
}
}
//查看详情
public function info()
{
$id = $this->request->param('id');
$info = $this->model->find($id);
return $this->success('获取成功', $info);
}
//webhook
public function webhook(){
$botkey = $this->request->param('botkey');
$id = $this->request->param('id');
$botid = explode(":", $botkey)[0];
$webhookdm = Request::domain() . '/api.Telegram/callback?botid='.$botid;
$Telegrambot=new Telegrambot($botkey);
$Telegrambot->setWebhook($webhookdm);
$me=$Telegrambot->getme();
$first_name=isset($me["result"]['first_name'])?$me["result"]['first_name']:"";
$username=isset($me["result"]['username'])?$me["result"]['username']:"";
$updateRes = $this->model->where('id', '=', $id)->update(['botid'=>$botid,'botuser'=>$username,'first_name'=>$first_name]);
if ($updateRes) {
return $this->success('操作成功');
} else if ($updateRes === 0) {
return $this->success('未作修改');
} else {
return $this->error('操作失败');
}
}
//编辑
public function edit(){
$id = $this->request->param('id');
$info = $this->model->find($id);
//var_dump($info);
$post = CommonFun::filterPostData($this->request->post());
$validate = new Edit();
if(!$validate->check($post)){
return $this->error($validate->getError());
}
foreach ($post as $k => $v) {
$info->$k = $v;
}
try {
$updateRes = $info->save();
if ($updateRes) {
return $this->success('编辑成功');
} else {
return $this->error('操作失败');
}
} catch (\Exception $e) {
return $this->exceptionError($e);
}
}
//设置账号状态
public function setStatus()
{
$id = $this->request->post('id');
$fieldVal = $this->request->post('field_val');
$isRecycle = $this->request->post('is_recycle');
$update['status'] = $fieldVal;
try {
if($isRecycle) {
$updateRes = $this->model->onlyTrashed()->where('id', '=', $id)->update($update);
} else {
$updateRes = $this->model->where('id', '=', $id)->update($update);
}
if ($updateRes) {
return $this->success('操作成功');
} else if ($updateRes === 0) {
return $this->success('未作修改');
} else {
return $this->error('操作失败');
}
} catch (\Exception $e) {
return $this->error('数据库异常,操作失败');
}
}
}
+151
View File
@@ -0,0 +1,151 @@
<?php
namespace app\controller\admin;
use app\service\admin\PluginsServiceFacade;
use laytp\controller\Backend;
use laytp\library\Http;
use think\facade\Config;
/**
* 插件管理
*/
class Plugins extends Backend
{
/**
* files模型对象
* @var \app\model\Files
*/
protected $model;
public $hasSoftDel = 1;//是否拥有软删除功能
// 获取laytp官网定义的插件分类列表
public function category()
{
$url = Config::get('plugin.apiUrl') . '/plugins/category';
$data = json_decode(Http::get($url), true)['data'];
return $this->success('获取成功', $data);
}
// 查看
public function index()
{
$params['page'] = $this->request->param('page', 1);
$params['limit'] = $this->request->param('limit', 10);
$params['category_id'] = $this->request->param('category_id', 0);
$url = Config::get('plugin.apiUrl') . '/plugins/index';
$data = json_decode(Http::get($url, $params), true)['data'];
$installed = Config::get('plugin.installed');
foreach ($data['data'] as $k => $datum) {
if ($installed && in_array($datum['alias'], $installed)) {
$data['data'][$k]['installed'] = 1;
$pluginInfo = PluginsServiceFacade::getPluginInfo($datum['alias']);
if (isset($pluginInfo['version'])) {
$version = $pluginInfo['version'];
} else {
$version = '1.0.0';
}
$data['data'][$k]['version'] = $version;
} else {
$data['data'][$k]['installed'] = 2;
}
}
return $this->success('获取成功', $data);
}
// 离线安装 - 所有的离线安装都是覆盖安装
public function offLineInstall()
{
if (PluginsServiceFacade::offLineInstall()) {
sleep(1);
$file = request()->file('laytpUploadFile'); // 获取上传的文件
$pluginConf = Config::get('plugin');
$fileName = $file->getOriginalName();
$pathinfo = pathinfo($fileName);
$plugin = $pathinfo['filename'];
$pluginConf['installed'][] = $plugin;
$pluginConf['installed'] = array_unique($pluginConf['installed']);
sort($pluginConf['installed']);
$info = PluginsServiceFacade::getPluginInfo($plugin);
if(isset($info['is_editor']) && $info['is_editor']){
$pluginConf['installedEditor'][] = $plugin;
$pluginConf['installedEditor'] = array_unique($pluginConf['installedEditor']);
sort($pluginConf['installedEditor']);
}
return $this->success('安装成功', [
'pluginConf' => $pluginConf
]);
} else {
return $this->error(PluginsServiceFacade::getError());
}
}
// 卸载
public function uninstall()
{
$plugin = $this->request->param('plugin');
$info = PluginsServiceFacade::getPluginInfo($plugin);
if (PluginsServiceFacade::unInstall($plugin)) {
$pluginConf = Config::get('plugin');
foreach($pluginConf['installed'] as $k=>$installed){
if($installed === $plugin) unset($pluginConf['installed'][$k]);
}
$pluginConf['installed'] = array_unique($pluginConf['installed']);
sort($pluginConf['installed']);
if(isset($info['is_editor']) && $info['is_editor']){
foreach($pluginConf['installedEditor'] as $k=>$installedEditor){
if($installedEditor === $plugin) unset($pluginConf['installedEditor'][$k]);
}
$pluginConf['installedEditor'] = array_unique($pluginConf['installedEditor']);
sort($pluginConf['installedEditor']);
// 如果根目录下有相应的html文件,将文件内容进行替换
$htmlFile = root_path() . 'public/admin/' . $plugin . '.html';
if(is_file($htmlFile)){
file_put_contents($htmlFile, '<p style="color:blue">请先到插件市场安装' . $plugin . '编辑器。</p>
<p style="color:red">注意:安装或卸载编辑器后,需要清空浏览器缓存才能生效</p>');
}
}
return $this->success($plugin . '插件卸载成功', [
'pluginConf' => $pluginConf,
'info' => $info
]);
} else {
return $this->error(PluginsServiceFacade::getError());
}
}
// 安装
public function install()
{
$plugin = $this->request->param('plugin');
$laytpGwToken = $this->request->param('laytpGwToken');
if (PluginsServiceFacade::install($plugin, $laytpGwToken)) {
// 如果是文件存储,插件安装信息,刚安装完成马上要取出来,取出来的不会是最新的文件内容
sleep(1);
$pluginConf = Config::get('plugin');
$pluginConf['installed'][] = $plugin;
$pluginConf['installed'] = array_unique($pluginConf['installed']);
sort($pluginConf['installed']);
$info = PluginsServiceFacade::getPluginInfo($plugin);
if(isset($info['is_editor']) && $info['is_editor']){
$pluginConf['installedEditor'][] = $plugin;
$pluginConf['installedEditor'] = array_unique($pluginConf['installedEditor']);
sort($pluginConf['installedEditor']);
}
return $this->success($plugin . '插件安装成功', [
'pluginConf' => $pluginConf
]);
} else {
$error = PluginsServiceFacade::getError();
return $this->error($error['msg'], $error['code']);
}
}
}
+177
View File
@@ -0,0 +1,177 @@
<?php
namespace app\controller\admin;
use laytp\controller\Backend;
use laytp\library\CommonFun;
use think\facade\Config;
use think\facade\Db;
use think\facade\Request;
/**
* 角色控制器
*/
class Role extends Backend
{
protected $noNeedAuth = ['getMenuIds'];
public $model;
public function _initialize()
{
$this->model = new \app\model\admin\Role();
}
/**
* 列表
* all_data参数的值为true时,表示查询表中所有数据集,否则进行分页查询
* @return mixed
*/
public function index()
{
$where = $this->buildSearchParams();
$order = $this->buildOrder();
$data = $this->model->where($where)->order($order);
$paging = $this->request->param('paging', false);
if ($paging) {
$limit = $this->request->param('limit', Config::get('paginate.limit'));
$data = $data->paginate($limit)->toArray();
$data['data'] = $this->getSelectedData($data['data']);
} else {
$data = $data->select()->toArray();
}
return $this->success('数据获取成功', $data);
}
/**
* 添加,同时添加lt_plugin_core_role和lt_plugin_core_role_menu两个表的数据
* @return \think\response\Json
*/
public function add()
{
Db::startTrans();
try {
$post = CommonFun::filterPostData($this->request->post());
$roleInfo = $this->model->getByName($post['name']);
if ($roleInfo) throw new \Exception('角色名已存在');
$menuIds = explode(',', $post['menu_ids']);
unset($post['menu_ids']);
$saveMenu = $this->model->save($post);
if (!$saveMenu) throw new \Exception('保存角色基本信息失败');
$saveAllData = [];
foreach ($menuIds as $menu_id) {
$saveAllData[] = [
'admin_role_id' => $this->model->id,
'admin_menu_id' => $menu_id,
];
}
$menu = new \app\model\admin\menu\Role();
$saveAllMenu = $menu->saveAll($saveAllData);
if (!$saveAllMenu) throw new \Exception('保存角色权限失败');
Db::commit();
return $this->success('操作成功');
} catch (\Exception $e) {
Db::rollback();
return $this->error('数据库异常,操作失败');
}
}
/**
* 编辑,同时编辑lt_plugin_core_role和lt_plugin_core_role_menu两个表的数据
* @return bool|\think\response\Json
*/
public function edit()
{
$id = $this->request->param('id');
$postData = Request::only(['id', 'name', 'menu_ids']);
$post = CommonFun::filterPostData($postData);
$roleInfo = $this->model->getByName($post['name']);
if ($roleInfo && ($roleInfo['id'] != $id)) {
return $this->error('角色名已存在');
}
Db::startTrans();
try {
$menuIds = explode(',', $post['menu_ids']);
unset($post['menu_ids']);
$updateRes = $this->model->where('id', '=', $id)->update($post);
if (!is_numeric($updateRes)) throw new \Exception('保存角色基本信息失败');
$delRes = \app\model\admin\menu\Role::where('admin_role_id', '=', $id)->delete();
if (!is_numeric($delRes)) throw new \Exception('删除角色权限失败');
$saveAllData = [];
foreach ($menuIds as $menu_id) {
$saveAllData[] = [
'admin_role_id' => $id,
'admin_menu_id' => $menu_id,
];
}
$menu = new \app\model\admin\menu\Role();
$menu->saveAll($saveAllData);
Db::commit();
return $this->success('操作成功');
} catch (\Exception $e) {
Db::rollback();
return $this->error('数据库异常,操作失败');
}
}
/**
* 真实删除
* lt_admin_role、lt_admin_menu_role、lt_admin_role_user三表数据都要真实删除
* @return \think\response\Json
*/
public function trueDel()
{
$ids = $this->request->param('ids');
Db::startTrans();
try {
$roles = $this->model->onlyTrashed()->where('id', 'in', $ids)->select();
foreach ($roles as $key => $item) {
$delRes = $item->force()->delete();
if (!$delRes) throw new \Exception('角色删除失败');
}
$delRes = \app\model\admin\menu\Role::where('admin_role_id', 'in', $ids)->delete();
if (!is_numeric($delRes)) throw new \Exception('角色权限删除失败');
$delRes = \app\model\admin\role\User::where('admin_role_id', 'in', $ids)->delete();
if (!is_numeric($delRes)) throw new \Exception('角色用户删除失败');
Db::commit();
return $this->success('操作成功');
} catch (\Exception $e) {
Db::rollback();
return $this->exceptionError($e);
}
}
/**
* 获取编辑页面权限设置应该选中的菜单id
* 只能获取最低级别的菜单id,有子菜单的菜单id不能返回
* 原因:比如tree.setChecked('auth_node',1);会选中整棵树,因为layui的树组件,模拟点击了树的首节点
* @return false|string|\think\response\Json
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getMenuIds()
{
$id = $this->request->param('id');
$menuIds = \app\model\admin\menu\Role::where('admin_role_id', '=', $id)->column('admin_menu_id');
$auth = [];
foreach ($menuIds as $menuId) {
$hasChild = \app\model\admin\Menu::where('pid', '=', $menuId)->find() ? true : false;
if (!$hasChild) {
$auth[] = $menuId;
}
}
return $this->success('获取成功', $auth);
}
}
+73
View File
@@ -0,0 +1,73 @@
<?php
namespace app\controller\admin;
use laytp\controller\Backend;
use think\facade\Config;
use laytp\library\CommonFun;
use app\validate\admin\bot\Add;
use app\validate\admin\bot\Edit;
use laytp\library\Str;
use think\facade\App;
use laytp\library\UploadDomain;
use app\service\Telegram as Telegrambot;
use think\facade\Request;
use GitWrapper\GitWrapper;
/**
* 会员管理
*/
class Updata extends Backend
{
/**
* member模型对象
* @var \app\model\Bot
*/
protected $model;
protected $hasSoftDel=1;//是否拥有软删除功能
protected $noNeedLogin = []; // 无需登录即可请求的方法
protected $noNeedAuth = ['index', 'info']; // 无需鉴权即可请求的方法
public function _initialize()
{
}
//查看和搜索列表
//查看和搜索列表
public function index(){
$where = $this->buildSearchParams();
$order = $this->buildOrder();
//var_dump($where,$order);
$data = $this->model->where($where)->order($order)->with(['avatar_pic_file']);
$paging = $this->request->param('paging', false);
if ($paging) {
$limit = $this->request->param('limit', Config::get('paginate.limit'));
$data = $data->paginate($limit)->toArray();
$data['data'] = $this->getSelectedData($data['data']);
} else {
$data = $data->select()->toArray();
}
return $this->success('数据获取成功', $data);
}
//添加
public function updata()
{
$repoPath = App::getRootPath();
$output = shell_exec("cd {$repoPath} && git pull 2>&1");
// 输出结果
echo "Git Pull Output:\n";
echo $output;
}
}
+306
View File
@@ -0,0 +1,306 @@
<?php
namespace app\controller\admin;
use app\model\admin\login\Log;
use app\service\admin\AuthServiceFacade;
use app\service\admin\UserServiceFacade;
use app\validate\admin\user\Add;
use app\validate\admin\user\Edit;
use app\validate\admin\user\Login;
use app\validate\admin\user\singleEdit;
use laytp\controller\Backend;
use laytp\library\CommonFun;
use laytp\library\Random;
use laytp\library\Str;
use laytp\library\Token;
use think\facade\Config;
use think\facade\Db;
use think\facade\Cache;
/**
* 后台管理员控制器
*/
class User extends Backend
{
protected $model;
//当前模型对象
protected $noNeedLogin = ['login', 'logout'];
protected $noNeedAuth = ['loginInfo', 'singleEdit'];
protected function _initialize()
{
$this->model = new \app\model\admin\User();
}
public function login()
{
//获取表单提交数据
$param = $this->request->post();
//防止密码爆破
$fail = Cache::get('laytp-admin-login-num-' . $param['username'], 1);
if ($fail >= 5) return $this->error('失败次数过多,请三分钟后再试');
//验证表单提交
$validate = new Login();
if (!$validate->check($param)) {
$param['password'] = '******';
//登录失败也不记录用户密码
Log::create([
'login_status' => 2,
'admin_id' => 0,
'request_body' => json_encode($param),
'request_header' => json_encode($this->request->header()),
'ip' => $this->request->ip(),
'create_time' => date('Y-m-d H:i:s'),
]);
Cache::set('laytp-admin-login-num-' . $param['username'], $fail + 1, 180);
return $this->error($validate->getError());
}
//设置登录信息
$loginUserInfo = \app\model\admin\User::where('username', '=', $param['username'])
->with(['avatar_file'])->field(UserServiceFacade::getAllowFields())->findOrEmpty();
$loginUserInfo->login_time = date('Y-m-d H:i:s');
$loginUserInfo->login_ip = $this->request->ip();
$loginUserInfo->save();
$userId = $loginUserInfo['id'];
$token = Random::uuid();
$loginUserInfo['token'] = $token;
Token::set($token, $userId, 24 * 60 * 60 * 3);
$param['password'] = '******';
//登录成功不记录用户密码
Log::create([
'login_status' => 1,
'admin_id' => $userId,
'request_body' => json_encode($param),
'request_header' => json_encode($this->request->header()),
'ip' => $this->request->ip(),
'create_time' => date('Y-m-d H:i:s'),
]);
$authList = AuthServiceFacade::getAuthList($userId);
return $this->success('登录成功', [
'user' => $loginUserInfo,
'authList' => $authList,
'pluginConf' => Config::get('plugin'),
]);
}
public function loginInfo()
{
$loginUserInfo = UserServiceFacade::getUserInfo();
$authList = AuthServiceFacade::getAuthList($loginUserInfo['id']);
return $this->success('获取成功', [
'user' => $loginUserInfo,
'authList' => $authList,
'pluginConf' => Config::get('plugin'),
'ltVersion' => LT_VERSION,
]);
}
//退出登录
public function logout()
{
$token = $this->request->header('laytp-admin-token', $this->request->cookie('laytpAdminToken'));
Token::delete($token);
return $this->success('退出成功');
}
//查看
public function index()
{
$where = $this->buildSearchParams();
$order = $this->buildOrder();
$data = $this->model->where($where)->order($order)->with(['avatar_file']);
$paging = $this->request->param('paging', false);
if ($paging) {
$limit = $this->request->param('limit', Config::get('paginate.limit'));
$data = $data->paginate($limit)->toArray();
$data['data'] = $this->getSelectedData($data['data']);
} else {
$data = $data->select()->toArray();
}
return $this->success('数据获取成功', $data);
}
//添加
public function add()
{
Db::startTrans();
try {
$post = CommonFun::filterPostData($this->request->post());
$validate = new Add();
if (!$validate->check($post)) throw new \Exception($validate->getError());
$post['password'] = Str::createPassword($post['password']);
$saveRes = $this->model->save($post);
if (!$saveRes) throw new \Exception('保存基础信息失败');
if ($post['role_ids']) {
$roleIds = explode(',', $post['role_ids']);
$data = [];
foreach ($roleIds as $k => $v) {
$data[] = ['admin_role_id' => $v, 'admin_user_id' => $this->model->id];
}
$roleUser = new \app\model\admin\role\User();
$saveAllRes = $roleUser->saveAll($data);
if (!$saveAllRes) throw new \Exception('保存角色信息失败');
}
Db::commit();
return $this->success('操作成功');
} catch (\Exception $e) {
Db::rollback();
return $this->error('数据库异常,操作失败');
}
}
//查看详情
public function info()
{
$id = $this->request->param('id');
$info = $this->model->with(['role_ids', 'avatar_file'])->findOrEmpty($id)->toArray();
$data = \app\resource\admin\User::info($info);
return $this->success('获取成功', $data);
}
//编辑
public function edit()
{
Db::startTrans();
try {
$post = CommonFun::filterPostData($this->request->post());
$user = $this->model->findOrEmpty($post['id']);
if (!$user) throw new \Exception('id参数错误');
$validate = new Edit();
if (!$validate->check($post)) throw new \Exception($validate->getError());
if ($post['password']) {
$post['password'] = Str::createPassword($post['password']);
} else {
unset($post['password']);
unset($post['re_password']);
}
$updateRes = $user->update($post);
if (!$updateRes) throw new \Exception('保存基本信息失败');
$userRole = new \app\model\admin\role\User();
$deleteRes = $userRole->where('admin_user_id', '=', $post['id'])->delete();
if (!is_numeric($deleteRes)) throw new \Exception('删除用户角色失败');
if ($post['role_ids']) {
$roleIds = explode(',', $post['role_ids']);
$data = [];
foreach ($roleIds as $k => $v) {
$data[] = ['admin_role_id' => $v, 'admin_user_id' => $user->id];
}
$saveAllRes = $userRole->saveAll($data);
if (!$saveAllRes) throw new \Exception('保存用户角色失败');
}
Db::commit();
return $this->success('操作成功');
} catch (\Exception $e) {
Db::rollback();
return $this->exceptionError($e);
}
}
//修改个人资料
public function singleEdit()
{
$post = CommonFun::filterPostData($this->request->post());
$validate = new singleEdit();
if (!$validate->check($post)) {
return $this->error($validate->getError());
}
if (!$post['password']) {
unset($post['password']);
} else {
$post['password'] = Str::createPassword($post['password']);
}
$user = $this->model->with(['avatar_file'])->find($post['id']);
if (!$user) {
return $this->error('ID参数错误');
}
$res = $user->update($post);
if ($res) {
return $this->success('操作成功');
} else {
return $this->error('操作失败');
}
}
//删除
public function del()
{
$ids = array_filter($this->request->param('ids'));
if (!$ids) {
return $this->error('参数ids不能为空');
}
if (in_array(1, $ids)) {
return $this->error('不允许删除初始化用户');
}
try {
if ($this->model->destroy($ids)) {
return $this->success('数据删除成功');
} else {
return $this->error('数据删除失败');
}
} catch (\Exception $e) {
return $this->exceptionError($e);
}
}
//设置状态
public function setStatus()
{
$id = $this->request->post('id');
$fieldVal = $this->request->post('field_val');
$isRecycle = $this->request->post('is_recycle');
$update['status'] = $fieldVal;
try {
if ($isRecycle) {
$updateRes = $this->model->onlyTrashed()->where('id', '=', $id)->update($update);
} else {
$updateRes = $this->model->where('id', '=', $id)->update($update);
}
if ($updateRes) {
return $this->success('操作成功');
} else if ($updateRes === 0) {
return $this->success('未作修改');
} else {
return $this->error('操作失败');
}
} catch (\Exception $e) {
return $this->error('数据库异常,操作失败');
}
}
//设置是否为超管
public function setIsSuperManager()
{
$id = $this->request->post('id');
$fieldVal = $this->request->post('field_val');
$isRecycle = $this->request->post('is_recycle');
$update['is_super_manager'] = $fieldVal;
try {
if ($isRecycle) {
$updateRes = $this->model->onlyTrashed()->where('id', '=', $id)->update($update);
} else {
$updateRes = $this->model->where('id', '=', $id)->update($update);
}
if ($updateRes) {
return $this->success('操作成功');
} else if ($updateRes === 0) {
return $this->success('未作修改');
} else {
return $this->error('操作失败');
}
} catch (\Exception $e) {
return $this->error('数据库异常,操作失败');
}
}
}
+66
View File
@@ -0,0 +1,66 @@
<?php
namespace app\controller\admin\action;
use laytp\controller\Backend;
use think\facade\Config;
/**
* 后台操作日志
*/
class Log extends Backend
{
/**
* admin_action_log模型对象
* @var \app\model\admin\action\Log
*/
protected $model;
protected $hasSoftDel = 0;//是否拥有软删除功能
protected $noNeedLogin = []; // 无需登录即可请求的方法
protected $noNeedAuth = ['index', 'info']; // 无需鉴权即可请求的方法
public function _initialize()
{
$this->model = new \app\model\admin\action\Log();
}
//查看和搜索列表
public function index()
{
$where = $this->buildSearchParams();
$order = $this->buildOrder();
$data = $this->model->where($where)->order($order)->with(['adminUser']);
$paging = $this->request->param('paging', false);
if ($paging) {
$limit = $this->request->param('limit', Config::get('paginate.limit'));
$data = $data->paginate($limit)->toArray();
$data['data'] = $this->getSelectedData($data['data']);
} else {
$data = $data->select()->toArray();
}
return $this->success('数据获取成功', $data);
}
//查看详情
public function info()
{
$id = $this->request->param('id');
$info = $this->model->find($id);
return $this->success('获取成功', $info);
}
//回收站
public function recycle()
{
$where = $this->buildSearchParams();
$order = $this->buildOrder();
$limit = $this->request->param('limit', Config::get('paginate.limit'));
$data = $this->model->onlyTrashed()
->with(['adminUser'])
->order($order)->where($where)->paginate($limit)->toArray();
return $this->success('回收站数据获取成功', $data);
}
}
+38
View File
@@ -0,0 +1,38 @@
<?php
namespace app\controller\admin\api;
use laytp\controller\Backend;
use think\facade\Config;
/**
* Api请求日志
*/
class Log extends Backend
{
/**
* api_log模型对象
* @var \app\model\api\Log
*/
protected $model;
protected $hasSoftDel = 0;//是否拥有软删除功能
protected $noNeedLogin = []; // 无需登录即可请求的方法
protected $noNeedAuth = ['index', 'info']; // 无需鉴权即可请求的方法
public function _initialize()
{
$this->model = new \app\model\api\Log();
}
//查看详情
public function info()
{
$id = $this->request->param('id');
$info = $this->model->find($id);
return $this->success('获取成功', $info);
}
}
+39
View File
@@ -0,0 +1,39 @@
<?php
namespace app\controller\admin\balance;
use laytp\controller\Backend;
use think\facade\Config;
/**
* Api请求日志
*/
class Log extends Backend
{
/**
* api_log模型对象app\model\admin\balance
* @var \app\model\admin\balance\Log
*/
protected $model;
protected $hasSoftDel = 0;//是否拥有软删除功能
protected $noNeedLogin = []; // 无需登录即可请求的方法
protected $noNeedAuth = ['index', 'info']; // 无需鉴权即可请求的方法
public function _initialize()
{
$this->model = new \app\model\admin\balance\Log();
}
//查看详情
public function info()
{
$id = $this->request->param('id');
$info = $this->model->find($id);
return $this->success('获取成功', $info);
}
}
+108
View File
@@ -0,0 +1,108 @@
<?php
namespace app\controller\admin\files;
use laytp\controller\Backend;
use laytp\library\Tree;
use think\facade\Config;
/**
* 附件分类管理
*/
class Category extends Backend
{
/**
* files_category模型对象
* @var \app\model\files\Category
*/
protected $model;
public $hasSoftDel = 1;//是否拥有软删除功能
public $orderRule = ['sort' => 'DESC', 'id' => 'ASC'];
public function initialize()
{
parent::initialize();
$this->model = new \app\model\files\Category();
}
//查看
public function index()
{
$where = $this->buildSearchParams();
$order = $this->buildOrder();
$sourceData = $this->model->order($order)->where($where);
$isTree = $this->request->param('is_tree');
if ($isTree) {
$menuTreeObj = Tree::instance();
$menuTreeObj->init($sourceData->select()->toArray());
$data = $menuTreeObj->getRootTrees();
} else {
$paging = $this->request->param('paging', false);
if ($paging) {
$limit = $this->request->param('limit', Config::get('paginate.limit'));
$data = $sourceData->paginate($limit)->toArray();
$data['data'] = $this->getSelectedData($data['data']);
} else {
$data = $sourceData->select()->toArray();
}
}
return $this->success('获取成功', $data);
}
//删除
public function del()
{
$ids = $this->request->post('ids');
if (!$ids) {
return $this->error('参数ids不能为空');
}
$sourceData = $this->model->select()->toArray();
$treeLib = Tree::instance();
$treeLib->init($sourceData);
$childIds = $treeLib->getChildIds($ids);
if ($this->model->destroy($childIds)) {
return $this->success('数据删除成功');
} else {
return $this->error('数据删除失败');
}
}
//回收站
public function recycle()
{
$where = $this->buildSearchParams();
$order = $this->buildOrder();
$limit = $this->request->param('limit', Config::get('paginate.limit'));
$data = $this->model->onlyTrashed()
->with(['parent'])
->order($order)->where($where)->paginate($limit)->toArray();
return $this->success('回收站数据获取成功', $data);
}
//设置排序
public function setSort()
{
$id = $this->request->post('id');
$fieldVal = $this->request->post('field_val');
$isRecycle = $this->request->post('is_recycle');
$update['sort'] = $fieldVal;
try {
if ($isRecycle) {
$updateRes = $this->model->onlyTrashed()->where('id', '=', $id)->update($update);
} else {
$updateRes = $this->model->where('id', '=', $id)->update($update);
}
if ($updateRes) {
return $this->success('操作成功');
} else if ($updateRes === 0) {
return $this->success('未作修改');
} else {
return $this->error('操作失败');
}
} catch (\Exception $e) {
return $this->error('数据库异常,操作失败');
}
}
}
+76
View File
@@ -0,0 +1,76 @@
<?php
namespace app\controller\admin\login;
use laytp\controller\Backend;
use laytp\library\CommonFun;
use think\facade\Config;
/**
* 后台登录日志
*/
class Log extends Backend
{
/**
* admin_login_log模型对象
* @var \app\model\admin\login\Log
*/
protected $model;
protected $hasSoftDel = 0;//是否拥有软删除功能
protected $noNeedLogin = []; // 无需登录即可请求的方法
protected $noNeedAuth = ['index', 'info']; // 无需鉴权即可请求的方法
public function _initialize()
{
$this->model = new \app\model\admin\login\Log();
}
//查看和搜索列表
/**
* @throws \think\db\exception\DbException
*/
public function index()
{
$where = $this->buildSearchParams();
$order = $this->buildOrder();
$data = $this->model->where($where)->order($order)->with(['adminUser']);
$paging = $this->request->param('paging', false);
if ($paging) {
$limit = $this->request->param('limit', Config::get('paginate.limit'));
$data = $data->paginate($limit)->toArray();
$data['data'] = $this->getSelectedData($data['data']);
} else {
$data = $data->select()->toArray();
}
return $this->success('数据获取成功', $data);
}
//查看详情
public function info()
{
$id = $this->request->param('id');
$info = $this->model->find($id);
return $this->success('获取成功', $info);
}
//回收站
public function recycle()
{
$where = $this->buildSearchParams();
$order = $this->buildOrder();
$limit = $this->request->param('limit', Config::get('paginate.limit'));
$data = $this->model->onlyTrashed()
->where($where)
->with(['adminUser'])
->order($order)->paginate($limit)->toArray();
return $this->success('回收站数据获取成功', $data);
}
}
+124
View File
@@ -0,0 +1,124 @@
<?php
namespace app\controller\api;
use think\facade\Db;
use laytp\controller\Api;
use dh2y\qrcode\QRcode;
use think\facade\Cache;
/**
* @ApiInternal ()
*/
class Demo extends Api
{
public $noNeedLogin = ['test'];
/*@formatter:on*/
/**
*
* @ApiTitle (需要登录的接口需要登录的接口需要登录的接口需要登录的接口)
* @ApiSummary (需要登录的接口详细描述)
* @ApiMethod (POST)
* @ApiRoute (/api.demo/test)
* @ApiHeaders (name="token", type="string", required="true", description="请求的Token")
* @ApiParams (name="id", type="integer", required="true", description="会员ID")
* @ApiParams (name="name", type="string", required="true", description="用户名")
* @ApiReturnParams (name="code", type="integer", description="接口返回码.0=常规正确码,表示常规操作成功;1=常规错误码,客户端仅需提示msg;其他返回码与具体业务相关。框架实现了的唯一其他返回码:10401,前端需要跳转至登录界面。在一个复杂的交互过程中,你可能需要自行定义其他返回码")
* @ApiReturnParams (name="msg", type="string", description="返回描述")
* @ApiReturnParams (name="time", type="integer", description="请求时间,Unix时间戳,单位秒")
* @ApiReturnParams (name="data", type="object", description="返回的数据对象")
* @ApiReturnParams (name="data.id", type="string", description="参数id的值")
* @ApiReturnParams (name="data.name", type="string", description="参数name的值")
* @ApiReturn
({
"code": 0,
"msg": "返回成功",
"time": 1591168410,
"data": {
"id": "",
"name": ""
}
})
*/
/*@formatter:on*/
public function test()
{ $req = ChangeMoney('7891454089', 'member', "-", 5, 8032909529, "消费" . "测试");
var_dump($req);
return $this->success('返回成功', $this->request->param());
}
/*@formatter:off*/
/**
* @ApiTitle (无需登录的接口)
* @ApiSummary (无需登录的接口详细描述)
* @ApiMethod (POST)
* @ApiRoute (/api.demo/test1)
* @ApiReturnParams (name="code", type="integer", description="接口返回码.0=常规正确码,表示常规操作成功;1=常规错误码,客户端仅需提示msg;其他返回码与具体业务相关。框架实现了的唯一其他返回码:10401,前端需要跳转至登录界面。在一个复杂的交互过程中,你可能需要自行定义其他返回码")
* @ApiReturnParams (name="msg", type="string", description="返回描述")
* @ApiReturnParams (name="time", type="integer", description="请求时间,Unix时间戳,单位秒")
* @ApiReturnParams (name="data", type="object", description="返回的数据对象")
* @ApiReturnParams (name="data.action", type="string", description="固定返回test1")
* @ApiReturn
({
"code": 0,
"msg": "返回成功",
"time": 1591168410,
"data": {
"action": "test1"
}
})
*/
/*@formatter:on*/
public function createqrcoe()
{
return $this->success('返回成功', ['action' => 'test1']);
}
/*@formatter:on*/
public function test1()
{
$public_path= public_path();
$code = new QRcode();
var_dump($public_path.'img/mode.png');
$code_path = $code->png('二维码携带的参数',$public_path.'uploads/qrcode/11122.png',10)->background(105,100,$public_path.'img/mode.jpg')->text("10.000",20,[250,480],'#000000')->text("TSCBYxKnLbLGPkcjZddWB4b64ymTdpcyY3",15,[140,550],'#000000')->getPath(true);
var_dump($code_path);
return $this->success('返回成功', ['action' => 'test1']);
}
/*@formatter:off*/
/**
* @ApiTitle (参数传递array的接口)
* @ApiSummary (参数传递array的接口详细描述)
* @ApiMethod (POST)
* @ApiRoute (/api.demo/arrayParam)
* @ApiParams (name="id", type="string", required="true", description="ID")
* @ApiParams (name="name", type="array", required="true", description="数组中的值")
* @ApiParams (name="array[key]", type="array", required="true", description="数组中的值")
* @ApiReturnParams (name="code", type="integer", description="接口返回码.0=常规正确码,表示常规操作成功;1=常规错误码,客户端仅需提示msg;其他返回码与具体业务相关。框架实现了的唯一其他返回码:10401,前端需要跳转至登录界面。在一个复杂的交互过程中,你可能需要自行定义其他返回码")
* @ApiReturnParams (name="msg", type="string", description="返回描述")
* @ApiReturnParams (name="time", type="integer", description="请求时间,Unix时间戳,单位秒")
* @ApiReturnParams (name="data", type="object", description="返回的数据对象")
* @ApiReturnParams (name="data.action", type="string", description="固定返回test1")
* @ApiReturn
({
"code": 0,
"msg": "返回成功",
"time": 1591168410,
"data": {
"action": "test1"
}
})
*/
/*@formatter:on*/
public function arrayParam()
{
return $this->success('返回成功', $this->request->param());
}
}
+16
View File
@@ -0,0 +1,16 @@
<?php
namespace app\controller\api;
use laytp\BaseController;
/**
* @ApiInternal ()
*/
class Index extends BaseController
{
public function index()
{
return redirect('/api.html');
}
}
+11
View File
File diff suppressed because one or more lines are too long
+11
View File
File diff suppressed because one or more lines are too long
+82
View File
@@ -0,0 +1,82 @@
<?php
namespace app\controller\api;
use laytp\controller\Api;
use laytp\library\Random;
/**
* @ApiInternal ()
*/
class Token extends Api
{
public $no_need_login = [];
/*@formatter:off*/
/**
* @ApiTitle (检测Token是否过期)
* @ApiSummary (检测Token是否过期)
* @ApiMethod (POST)
* @ApiRoute (/api.token/check)
* @ApiHeaders (name="token", type="string", required="true", description="用户登录后得到的Token")
* @ApiReturnParams (name="code", type="integer", description="接口返回码.0=常规正确码,表示常规操作成功;1=常规错误码,客户端仅需提示msg;其他返回码与具体业务相关。框架实现了的唯一其他返回码:10401,前端需要跳转至登录界面。在一个复杂的交互过程中,你可能需要自行定义其他返回码")
* @ApiReturnParams (name="msg", type="string", description="返回描述")
* @ApiReturnParams (name="time", type="integer", description="请求时间,Unix时间戳,单位秒")
* @ApiReturnParams (name="data", type="object", description="返回的数据对象")
* @ApiReturnParams (name="data.token", type="string", description="用户登录凭证,token")
* @ApiReturnParams (name="data.expires_in", type="integer", description="token有效时间,单位秒")
* @ApiReturn
({
"code": 0,
"msg": "Token有效",
"time": 1591167181,
"data": {
"token": "827fb87e-2064-45c8-839a-128e195a7411",
"expires_in": 1789
}
})
*/
/*@formatter:on*/
public function check()
{
$token = $this->service_user->getToken();
$tokenInfo = \library\Token::get($token);
$this->success('Token有效', ['token' => $tokenInfo['token'], 'expires_in' => $tokenInfo['expires_in']]);
}
/*@formatter:off*/
/**
* @ApiTitle (刷新Token)
* @ApiSummary (刷新Token)
* @ApiMethod (POST)
* @ApiRoute (/api.token/refresh)
* @ApiHeaders (name="token", type="string", required="true", description="用户登录后得到的Token")
* @ApiReturnParams (name="code", type="integer", description="接口返回码.0=常规正确码,表示常规操作成功;1=常规错误码,客户端仅需提示msg;其他返回码与具体业务相关。框架实现了的唯一其他返回码:10401,前端需要跳转至登录界面。在一个复杂的交互过程中,你可能需要自行定义其他返回码")
* @ApiReturnParams (name="msg", type="string", description="返回描述")
* @ApiReturnParams (name="time", type="integer", description="请求时间,Unix时间戳,单位秒")
* @ApiReturnParams (name="data", type="object", description="返回的数据对象")
* @ApiReturnParams (name="data.token", type="string", description="用户登录凭证,token")
* @ApiReturnParams (name="data.expires_in", type="integer", description="token有效时间,单位秒")
* @ApiReturn
({
"code": 0,
"msg": "成功刷新Token",
"time": 1591167423,
"data": {
"token": "e356df60-ff03-4f15-bb66-c0e3ef37f335",
"expires_in": 1800
}
})
*/
/*@formatter:on*/
public function refresh()
{
//删除源Token
$token = $this->service_user->getToken();
\library\Token::delete($token);
//创建新Token
$token = Random::uuid();
\library\Token::set($token, $this->service_user->id, $this->service_user->token_keep_time);
$this->success('成功刷新Token', ['token' => $token, 'expires_in' => $this->service_user->token_keep_time]);
}
}
+88
View File
@@ -0,0 +1,88 @@
<?php
namespace app\exception;
use app\model\api\Log;
use think\exception\Handle;
use Throwable;
/**
* 异常处理接管
* 解决的问题:
* 1.记录数据库日志
* 2.windows环境下,出现异常时,页面输出空白的问题
* Class Http
* @package app\exception
*/
class Http extends Handle
{
/**
* 不需要记录信息(日志)的异常类列表
* 定义为空,说明是所有异常全部都记录
* @var array
*/
protected $ignoreReport = [];
/**
* 记录异常信息(包括日志或者其它方式记录)
*
* @access public
* @param Throwable $exception
* @return void
*/
public function report(Throwable $exception): void
{
// 非命令行下,记录错误日志
if(PHP_SAPI !== 'cli') {
$request = request();
// 记录数据库日志
// 这里必须要try,不然,如果报数据库错误,系统会循环调用异常接管程序,导致500
try{
Log::create([
'rule' => $request->url(),
'request_body' => json_encode(request()->post(), JSON_UNESCAPED_UNICODE),
'request_header' => json_encode(request()->header(), JSON_UNESCAPED_UNICODE),
'ip' => $request->ip(),
'status_code' => 500,
'response_body' => json_encode($this->convertExceptionToArray($exception), JSON_UNESCAPED_UNICODE),
'create_time' => date('Y-m-d H:i:s'),
]);
}catch (\Exception $e){
}
}
// 使用内置的方式记录异常日志
parent::report($exception);
}
/**
* 收集异常数据
* 重写父类此方法,将Server/Request Data进行转码,解决windows环境下,出现异常时,页面输出空白的问题
* @param Throwable $exception
* @return array
*/
protected function convertExceptionToArray(Throwable $exception): array
{
$data = parent::convertExceptionToArray($exception);
$data['tables']['Server/Request Data'] = $this->changeToUtf8($this->app->request->server());
return $data;
}
/**
* 将获取的服务器信息中的中文编码转为utf-8
* 修复在开启debug模式时出现的Malformed UTF-8 characters 错误
* @access protected
* @param $data array
* @return array 转化后的数组
*/
protected function changeToUtf8(array $data): array
{
foreach ($data as $key => $value) {
$data[$key] = is_array($value)?$this->changeToUtf8($value):mb_convert_encoding($value, "UTF-8", mb_detect_encoding($value));
}
return $data;
}
}
+1
View File
@@ -0,0 +1 @@
{"code":0,"msg":"请求出现异常","data":{}}
+11
View File
@@ -0,0 +1,11 @@
<?php
// 全局中间件定义文件
return [
// 全局请求缓存
// \think\middleware\CheckRequestCache::class,
// 多语言加载
// \think\middleware\LoadLangPack::class,
// Session初始化
\think\middleware\SessionInit::class,
\app\middleware\AllowCrossDomain::class,
];
+31
View File
@@ -0,0 +1,31 @@
<?php
namespace app\middleware;
use laytp\BaseMiddleware;
use think\Request;
/**
* 允许跨域中间件
* Class Auth
* @package app\middleware
*/
class AllowCrossDomain extends BaseMiddleware
{
/**
* 执行中间件
* @param Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, \Closure $next)
{
$header = [
'Access-Control-Allow-Origin' => '*',
'Access-Control-Allow-Methods' => 'GET, POST, PATCH, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers' => '*',
];
return $next($request)->header($header);
}
}
+69
View File
@@ -0,0 +1,69 @@
<?php
/**
* 后台操作日志中间件
*/
namespace app\middleware\admin;
use app\model\admin\action\Log;
use laytp\BaseMiddleware;
use app\model\admin\Menu;
use app\service\admin\UserServiceFacade;
use think\Request;
class ActionLog extends BaseMiddleware
{
/**
* 执行中间件
* @param Request $request
* @param \Closure $next
* @return mixed
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function handle(Request $request, \Closure $next)
{
$initUser = UserServiceFacade::init($request->header('laytp-admin-token', $request->cookie('laytpAdminToken')));
if (!$initUser || $request->isGet()) return $next($request);
$requestBody = $request->post();
//日志不记录密码
if (isset($requestBody['password'])) {
$requestBody['password'] = '******';
}
$rule = $request->url();
$menu = '';
$menus = Menu::where('rule', '=', $rule)->order('pid', 'asc')->select()->toArray();
if ($menus) {
for ($i = 1; $i <= 4; $i++) {
if ($menus[0]['pid']) {
$tempMenu = Menu::where('id', '=', $menus[0]['pid'])->find();
if ($tempMenu) {
$tempMenu = $tempMenu->toArray();
array_unshift($menus, $tempMenu);
}
} else {
break;
}
}
$menuName = [];
foreach ($menus as $v) {
$menuName[] = $v['name'];
}
$menu = implode(' - ', $menuName);
}
Log::create([
'admin_id' => UserServiceFacade::getUser()->id,
'rule' => $request->url(),
'menu' => $menu,
'request_body' => json_encode($requestBody, JSON_UNESCAPED_UNICODE),
'request_header' => json_encode(request()->header(), JSON_UNESCAPED_UNICODE),
'ip' => request()->ip(),
'create_time' => date('Y-m-d H:i:s'),
]);
return $next($request);
}
}
+53
View File
@@ -0,0 +1,53 @@
<?php
namespace app\middleware\admin;
use app\service\admin\AuthServiceFacade;
use app\service\admin\UserServiceFacade;
use laytp\BaseMiddleware;
use think\Request;
class Auth extends BaseMiddleware
{
/**
* 执行中间件
* @param Request $request
* @param \Closure $next
* @return mixed
*/
public function handle(Request $request, \Closure $next)
{
if (AuthServiceFacade::needLogin()) {
// 为了兼容windows和linux获取header中驼峰命名的参数修改
// windows中,使用原样获取,比如laytpAdminToken
// linux中,驼峰需要转换成短横杆获取,比如laytpAdminToken要使用laytp-admin-token来获取
$initUser = UserServiceFacade::init($request->header('laytp-admin-token', $request->header('laytpAdminToken', $request->cookie('laytpAdminToken'))));
if (!$initUser) return $this->error(UserServiceFacade::getError(), 10401);
if (!UserServiceFacade::isLogin()) {
if ($request->isAjax()) {
return $this->error('登录信息已过期', 10401);
} else {
return redirect('/admin/login.html');
}
}
if (AuthServiceFacade::needAuth()) {
$user = UserServiceFacade::getUser();
if ($user->is_super_manager !== 1) {
$userId = $user->id;
$plugin = defined('LT_PLUGIN') ? LT_PLUGIN : '';
$controller = strtolower(str_replace("\\", ".", $request->controller()));
if ($plugin) {
$node = 'plugin/' . $plugin . '/' . $controller . '/' . $request->action();
} else {
$node = trim(app('http')->getName() . '/' . $controller . '/' . $request->action(),'/');
}
if (!AuthServiceFacade::hasAuth($userId, $node)) {
return $this->error('无权请求:/' . $node);
}
}
}
}
return $next($request);
}
}
+32
View File
@@ -0,0 +1,32 @@
<?php
namespace app\middleware\api;
use app\service\api\AuthServiceFacade;
use app\service\api\MemberServiceFacade;
use laytp\BaseMiddleware;
use think\Request;
class Auth extends BaseMiddleware
{
/**
* 执行中间件
* @param Request $request
* @param \Closure $next
* @return mixed
*/
public function handle(Request $request, \Closure $next)
{
if (AuthServiceFacade::needLogin()) {
$initUser = MemberServiceFacade::init($request->header('token'));
if (!$initUser) return $this->error(MemberServiceFacade::getError(), 10401);
if (!MemberServiceFacade::isLogin()) {
return $this->error('登录信息已过期', 10401);
}
} else {
//不需要登录的接口,也可能需要获取登录用户的信息
MemberServiceFacade::init($request->header('token'));
}
return $next($request);
}
}
+60
View File
@@ -0,0 +1,60 @@
<?php
namespace app\middleware\api;
use app\service\api\CheckSignServiceFacade;
use app\service\ConfServiceFacade;
use laytp\BaseMiddleware;
use laytp\traits\JsonReturn;
use think\Request;
/**
* 签名验证中间件
* 签名生成方式:strtoupper(md5(md5($hearder['request-time']).md5(Config::get('laytp.api.signKey'))))
* 签名验证方式:
* 请求的header头中需要有两个参数:
* - request-time = 当前请求的时间,这个单位并不要求固定,甚至传的都可以不是当前请求的时间,只要sign的值是使用这个参数的值按照规定的算法生成即可
* - sign = md5(md5($hearder['request-time']).md5(Config::get('laytp.api.signKey')))
* 此中间件,使用header头中request-time按照签名算法生成签名,然后与header中传递的sign参数比对,看是否一致,如果一致就通过验证,如果不一致就验证失败
* 需要设计,有些方法无需验签。比如获取配置的接口,需要得到后台配置的Config::get('laytp.api.signKey')
* Class CheckSign
* @package app\middleware
*/
class CheckSign extends BaseMiddleware
{
use JsonReturn;
/**
* 执行中间件
* @param Request $request
* @param \Closure $next
* @return mixed
*/
public function handle(Request $request, \Closure $next)
{
if (intval(ConfServiceFacade::get('system.basic.checkSign')) === 1) {
if (CheckSignServiceFacade::needCheckSign()) {
if (CheckSignServiceFacade::check()) {
return $next($request);
} else {
if(env('APP_DEBUG')){
return $this->error('签名验证失败',0, [
'签名验证的规则描述' => '1. 后台系统配置->基础配置,Api签名开关需要打开,Api签名的Key需要设置(允许为空);2. 客户端在请求Api接口时,需要在header里面传递两个参数,request-time和signsign的值要等于strtoupper(md5(md5(header:request-time).md5(conf:signKey)))',
'注意点' => '如果是使用的生成Api文档在线测试接口,请点击右上角Api请求配置将签名验证开关打开',
'生成签名的规则' => 'strtoupper(md5(md5(header:request-time).md5(conf:signKey)))',
'系统配置的生成签名的Key' => ConfServiceFacade::get('system.basic.signKey'),
'header部分传递的request-time值' => $request->header('request-time'),
'header部分传递的sign值' => $request->header('sign'),
'后端计算的sign值' => CheckSignServiceFacade::getError()
]);
}
return $this->error(CheckSignServiceFacade::getError());
}
} else {
return $next($request);
}
} else {
return $next($request);
}
}
}
+46
View File
@@ -0,0 +1,46 @@
<?php
/**
* 地区管理模型
*/
namespace app\model;
use laytp\BaseModel;
use think\model\concern\SoftDelete;
class Area extends BaseModel
{
use SoftDelete;
//模型名
protected $name = 'area';
//附加属性
protected $append = [];
//时间戳字段转换
//表名
//关联模型
public function parent(){
return $this->belongsTo('app\model\Area','pid','id');
}
//新增属性的方法
public function getCreateTimeIntAttr($value, $data)
{
return isset($data['create_time']) ? strtotime($data['create_time']) : 0;
}
public function getUpdateTimeIntAttr($value, $data)
{
return isset($data['update_time']) ? strtotime($data['update_time']) : 0;
}
public function getDeleteTimeIntAttr($value, $data)
{
return isset($data['delete_time']) ? strtotime($data['delete_time']) : 0;
}
}
+60
View File
@@ -0,0 +1,60 @@
<?php
/**
* 会员管理模型
*/
namespace app\model;
use laytp\BaseModel;
use think\model\concern\SoftDelete;
class Bot extends BaseModel
{
use SoftDelete;
//模型名
protected $name = 'admin_bot';
//附加属性
protected $append = ['create_time_int','update_time_int','delete_time_int'];
//时间戳字段转换
protected $type = [
'create_time_int' => 'timestamp:Y-m-d H:i:s',
'update_time_int' => 'timestamp:Y-m-d H:i:s',
'delete_time_int' => 'timestamp:Y-m-d H:i:s',
];
//表名
//关联模型
public function avatarPicFile(){
return $this->belongsTo('app\model\Files','avatar_pic','id');
}
//新增属性的方法
public function getVipTimeIntAttr($value, $data)
{
return isset($data['vip_time']) ? $data['vip_time'] : 0;
}
public function getLoginTimeIntAttr($value, $data)
{
return isset($data['login_time']) ? strtotime($data['login_time']) : 0;
}
public function getCreateTimeIntAttr($value, $data)
{
return isset($data['create_time']) ? strtotime($data['create_time']) : 0;
}
public function getUpdateTimeIntAttr($value, $data)
{
return isset($data['update_time']) ? strtotime($data['update_time']) : 0;
}
public function getDeleteTimeIntAttr($value, $data)
{
return isset($data['delete_time']) ? strtotime($data['delete_time']) : 0;
}
}
+13
View File
@@ -0,0 +1,13 @@
<?php
/**
* 系统配置模型
*/
namespace app\model;
use laytp\BaseModel;
class Conf extends BaseModel
{
protected $name = 'conf';
}
+57
View File
@@ -0,0 +1,57 @@
<?php
/**
* 附件管理模型
*/
namespace app\model;
use laytp\BaseModel;
use think\model\concern\SoftDelete;
use laytp\library\UploadDomain;
class Files extends BaseModel
{
use SoftDelete;
//模型名
protected $name = 'files';
//时间戳字段转换
//表名
//关联模型
public function category(){
return $this->belongsTo('app\model\files\Category','category_id','id');
}
public function createAdminUser(){
return $this->belongsTo('app\model\admin\User','create_admin_user_id','id');
}
public function updateAdminUser(){
return $this->belongsTo('app\model\admin\User','update_admin_user_id','id');
}
//新增属性的方法
public function getPathAttr($value, $data)
{
return $value ? UploadDomain::singleAddUploadDomain($data) : '';
}
public function getCreateTimeIntAttr($value, $data)
{
return isset($data['create_time']) ? strtotime($data['create_time']) : 0;
}
public function getUpdateTimeIntAttr($value, $data)
{
return isset($data['update_time']) ? strtotime($data['update_time']) : 0;
}
public function getDeleteTimeIntAttr($value, $data)
{
return isset($data['delete_time']) ? strtotime($data['delete_time']) : 0;
}
}
+58
View File
@@ -0,0 +1,58 @@
<?php
/**
* 会员管理模型
*/
namespace app\model;
use laytp\BaseModel;
use think\model\concern\SoftDelete;
class Member extends BaseModel
{
use SoftDelete;
//模型名
protected $name = 'member';
//附加属性
protected $append = ['vip_time_int','login_time_int','create_time_int','update_time_int','delete_time_int'];
//时间戳字段转换
protected $type = [
'vip_time' => 'timestamp:Y-m-d H:i:s',
];
//表名
//关联模型
public function avatarPicFile(){
return $this->belongsTo('app\model\Files','avatar_pic','id');
}
//新增属性的方法
public function getVipTimeIntAttr($value, $data)
{
return isset($data['vip_time']) ? $data['vip_time'] : 0;
}
public function getLoginTimeIntAttr($value, $data)
{
return isset($data['login_time']) ? strtotime($data['login_time']) : 0;
}
public function getCreateTimeIntAttr($value, $data)
{
return isset($data['create_time']) ? strtotime($data['create_time']) : 0;
}
public function getUpdateTimeIntAttr($value, $data)
{
return isset($data['update_time']) ? strtotime($data['update_time']) : 0;
}
public function getDeleteTimeIntAttr($value, $data)
{
return isset($data['delete_time']) ? strtotime($data['delete_time']) : 0;
}
}
+13
View File
@@ -0,0 +1,13 @@
<?php
/**
* 数据库迁移模型
*/
namespace app\model;
use laytp\BaseModel;
class Migrations extends BaseModel
{
protected $name = 'migrations';
}
+38
View File
@@ -0,0 +1,38 @@
<?php
/**
* 会员管理模型
*/
namespace app\model;
use laytp\BaseModel;
use think\model\concern\SoftDelete;
class Order extends BaseModel
{
use SoftDelete;
//模型名
protected $name = 'order';
//附加属性
protected $append = ['create_time','end_time'];
//时间戳字段转换
protected $type = [
'end_time' => 'timestamp:Y-m-d H:i:s',
'create_time' => 'timestamp:Y-m-d H:i:s',
];
//表名
//关联模型
public function avatarPicFile(){
return $this->belongsTo('app\model\Files','avatar_pic','id');
}
}
+94
View File
@@ -0,0 +1,94 @@
<?php
namespace app\model;
use think\facade\Db;
use think\facade\Cache;
use think\facade\Log;
class TronSdk
{
private $domain;
private $username;
public function __construct()
{
$this->domain = "http://tron.konline.top:888/";
$this->username= "kaihekaiyuan";
$this->token= "a782a1f489d32687fd9f67f42f35e9e1";
}
//setwebhook
public function setwebhook2($webhookurl, $trcadd){
$func= $this->domain."custom/webhook";
// $webhookurl='http://' . env('proxyDomain', 'false') . '/customer/PayApi/pay?type='.$mode;
$data=['username'=>$this->username,'address'=>$trcadd,"url"=>$webhookurl];
$sing= md5($this->username.$trcadd.$webhookurl.$this->token);
$data['sign']=$sing;
return json_decode(curl_post($func,http_build_query($data)),true);
}
public function setwebhook($mode, $trcadd) //模块 订阅地址
{ $func= $this->domain."custom/webhook";
$webhookurl='http://' . env('proxyDomain', 'false') . '/customer/PayApi/pay?type='.$mode;
$data=['username'=>$this->username,'address'=>$trcadd,"url"=>$webhookurl];
$sing= md5($this->username.$trcadd.$webhookurl.$this->token);
$data['sign']=$sing;
return json_decode(curl_post($func,http_build_query($data)),true);
}
//delete_webhook
public function delete_webhook($address){ //删除订阅
$func= $this->domain."custom/delete_webhook";
$data=['username'=>$this->username,'address'=>$address];
$sing= md5($this->username.$address.$this->token);
$data['sign']=$sing;
return json_decode(curl_post($func,http_build_query($data)),true);
}
public function up_webhook($address,$weburl){ //更新订阅
$func= $this->domain."custom/up_webhook";
$data=['username'=>$this->username,'url'=>$weburl,'address'=>$address];
$sing= md5($this->username.$address.$weburl.$this->token);
$data['sign']=$sing;
return json_decode(curl_post($func,http_build_query($data)),true);
}
public function queryhook($address,$cache=null) //查询订阅
{ $func= $this->domain."custom/query";
$data=['username'=>$this->username,'address'=>$address];
$sing= md5($this->username.$address.$this->token);
$data['sign']=$sing;
if(empty($cache)){
$cache= cache::get("custom/query".$address);
if(!empty($cache)){
// var_dump("99999");
return $cache;
}else{
$cache=json_decode(curl_post($func,http_build_query($data)),true);
cache::set("custom/query".$address,$cache,1600);
return $cache;
}
}else{
$cache=json_decode(curl_post($func,http_build_query($data)),true);
cache::set("custom/query".$address,$cache,1600);
return $cache;
}
}
//查询地址是否合规
public function checkaddress($address){
$url="https://apilist.tronscanapi.com/api/multiple/chain/query?address=".$address;//
$data=json_decode(curl_get($url),true);
$publicTag=isset($data['publicTag'])?$data['publicTag']:"";
//var_dump($data);
if(empty($publicTag)){
return true;
}else{
return false;
}
}
}
+63
View File
@@ -0,0 +1,63 @@
<?php
/**
* 会员管理模型
*/
namespace app\model;
use laytp\BaseModel;
use think\model\concern\SoftDelete;
class User extends BaseModel
{
use SoftDelete;
//模型名
protected $name = 'user';
//附加属性
protected $append = ['create_time','update_time','delete_time','vip_time','login_time'];
//时间戳字段转换
protected $type = [
// 'create_time' => 'timestamp:Y-m-d H:i:s',
// 'update_time' => 'timestamp:Y-m-d H:i:s',
// 'delete_time' => 'timestamp:Y-m-d H:i:s',
// 'vip_time' => 'timestamp:Y-m-d H:i:s',
// 'login_time' => 'timestamp:Y-m-d H:i:s',
];
//表名
//关联模型
public function avatarPicFile(){
return $this->belongsTo('app\model\Files','avatar_pic','id');
}
//新增属性的方法
public function getVipTimeIntAttr($value, $data)
{
return isset($data['vip_time']) ? $data['vip_time'] : 0;
}
public function getLoginTimeIntAttr($value, $data)
{
return isset($data['login_time']) ? strtotime($data['login_time']) : 0;
}
public function getCreateTimeIntAttr($value, $data)
{
return isset($data['create_time']) ? strtotime($data['create_time']) : 0;
}
public function getUpdateTimeIntAttr($value, $data)
{
return isset($data['update_time']) ? strtotime($data['update_time']) : 0;
}
public function getDeleteTimeIntAttr($value, $data)
{
return isset($data['delete_time']) ? strtotime($data['delete_time']) : 0;
}
}
+18
View File
@@ -0,0 +1,18 @@
<?php
/**
* 后台菜单模型
*/
namespace app\model\admin;
use laytp\BaseModel;
use think\model\concern\SoftDelete;
class Menu extends BaseModel
{
use SoftDelete;
protected $name = 'admin_menu';
protected $append = [];
}
+16
View File
@@ -0,0 +1,16 @@
<?php
/**
* 角色模型
*/
namespace app\model\admin;
use laytp\BaseModel;
use think\model\concern\SoftDelete;
class Role extends BaseModel
{
use SoftDelete;
protected $name = 'admin_role';
}
+52
View File
@@ -0,0 +1,52 @@
<?php
/**
* 后台管理员表模型
*/
namespace app\model\admin;
use laytp\BaseModel;
use laytp\library\UploadDomain;
use think\model\concern\SoftDelete;
class User extends BaseModel
{
use SoftDelete;
//模型名
protected $name = 'admin_user';
//数组常量
public $const = [
'is_super_manager' => [
'2' => '否'
, '1' => '是',
],
'status' => [
'2' => '禁用'
, '1' => '正常',
],
];
public function avatarFile(){
return $this->belongsTo('app\model\Files','avatar','id');
}
// 定义默认头像
public function getAvatarFileAttr($value){
if(!$value){
return [
'id' => "",
'filename' => '默认头像',
'path' => UploadDomain::getDefaultAvatar(),
];
}else{
return $value;
}
}
public function roleIds()
{
return $this->hasMany(\app\model\admin\role\User::class, 'admin_user_id');
}
}
+34
View File
@@ -0,0 +1,34 @@
<?php
/**
* 后台操作日志模型
*/
namespace app\model\admin\action;
use laytp\BaseModel;
class Log extends BaseModel
{
//模型名
protected $name = 'admin_action_log';
//附加属性
protected $append = [];
//时间戳字段转换
//表名
//关联模型
public function adminUser(){
return $this->belongsTo('app\model\admin\User','admin_id','id');
}
//新增属性的方法
public function getCreateTimeIntAttr($value, $data)
{
return isset($data['create_time']) ? strtotime($data['create_time']) : 0;
}
}
+34
View File
@@ -0,0 +1,34 @@
<?php
/**
* Api请求日志模型
*/
namespace app\model\admin\balance;
use laytp\BaseModel;
class Log extends BaseModel
{
//模型名
protected $name = 'balance';
//附加属性
protected $append = [];
//时间戳字段转换
protected $type = [
'time' => 'timestamp:Y-m-d H:i:s',
];
//表名
//关联模型
//新增属性的方法
public function getCreateTimeIntAttr($value, $data)
{
return isset($data['create_time']) ? strtotime($data['create_time']) : 0;
}
}
+34
View File
@@ -0,0 +1,34 @@
<?php
/**
* 后台登录日志模型
*/
namespace app\model\admin\login;
use laytp\BaseModel;
class Log extends BaseModel
{
//模型名
protected $name = 'admin_login_log';
//附加属性
protected $append = [];
//时间戳字段转换
//表名
//关联模型
public function adminUser(){
return $this->belongsTo('app\model\admin\User','admin_id','id');
}
//新增属性的方法
public function getCreateTimeIntAttr($value, $data)
{
return isset($data['create_time']) ? strtotime($data['create_time']) : 0;
}
}
+13
View File
@@ -0,0 +1,13 @@
<?php
/**
* 菜单与角色关系模型
*/
namespace app\model\admin\menu;
use think\model\Pivot;
class Role extends Pivot
{
protected $name = 'admin_menu_role';
}
+14
View File
@@ -0,0 +1,14 @@
<?php
/**
* 角色与用户关系
*/
namespace app\model\admin\role;
use think\model\Pivot;
class User extends Pivot
{
protected $name = 'admin_role_user';
}
+32
View File
@@ -0,0 +1,32 @@
<?php
/**
* Api请求日志模型
*/
namespace app\model\api;
use laytp\BaseModel;
class Log extends BaseModel
{
//模型名
protected $name = 'api_log';
//附加属性
protected $append = [];
//时间戳字段转换
//表名
//关联模型
//新增属性的方法
public function getCreateTimeIntAttr($value, $data)
{
return isset($data['create_time']) ? strtotime($data['create_time']) : 0;
}
}
+43
View File
@@ -0,0 +1,43 @@
<?php
/**
* 附件分类管理模型
*/
namespace app\model\files;
use laytp\BaseModel;
use think\model\concern\SoftDelete;
class Category extends BaseModel
{
use SoftDelete;
//模型名
protected $name = 'files_category';
//时间戳字段转换
//表名
//关联模型
public function parent(){
return $this->belongsTo('app\model\files\Category','pid','id');
}
//新增属性的方法
public function getCreateTimeIntAttr($value, $data)
{
return isset($data['create_time']) ? strtotime($data['create_time']) : 0;
}
public function getUpdateTimeIntAttr($value, $data)
{
return isset($data['update_time']) ? strtotime($data['update_time']) : 0;
}
public function getDeleteTimeIntAttr($value, $data)
{
return isset($data['delete_time']) ? strtotime($data['delete_time']) : 0;
}
}
+7
View File
@@ -0,0 +1,7 @@
<?php
use app\exception\Http;
// 容器Provider定义文件
return [
'think\exception\Handle' => Http::class,
];
+28
View File
@@ -0,0 +1,28 @@
<?php
namespace app\resource\admin;
use laytp\Resource;
/**
* 用户资源
*/
class User extends Resource
{
/**
* 用户首页列表需要将关联模型取到的role_ids转换成以逗号分隔的字符串
* @param $data
* @return mixed
*/
static public function info($data)
{
$roleIdsArr = [];
if($data){
foreach ($data['role_ids'] as $k => $v) {
$roleIdsArr[] = $v['admin_role_id'];
}
$data['role_ids'] = implode(',', $roleIdsArr);
}
return $data;
}
}
+215
View File
@@ -0,0 +1,215 @@
<?php
namespace app\service;
use laytp\library\UploadDomain;
use laytp\traits\Error;
use think\facade\Cache;
use think\facade\Config;
/**
* 系统配置服务器实现者
* Class Auth
* @package app\service
*/
class Conf
{
use Error;
// 是否使用redis,如果不想使用redis,修改此处为false即可
protected $useRedis = true;
// 数据库连接句柄
protected $db = null;
//判断是否配置了redis
protected function hasRedis(){
$redisConf = Config::get('cache');
if(isset($redisConf['stores']['redis']['type']) && $this->useRedis){
return true;
}
return false;
}
/**
* 通过一个完整的key,获取配置信息
* @param $wholeKey
* @param $defaultValue
* @return bool|mixed|string
*/
public function get($wholeKey, $defaultValue='')
{
if($this->hasRedis()){
$redis = Cache::store('redis')->handler();
if($redis){
$value = $redis->hget($wholeKey, 'value');
$formType = $redis->hget($wholeKey, 'form_type');
if($formType == 'array') return json_decode($value, JSON_UNESCAPED_UNICODE);
return $value ? $value : $defaultValue;
}
}
list($group, $key) = $this->getGroupKey($wholeKey);
$conf = \app\model\Conf::where(['group'=>$group, 'key'=>$key])->findOrEmpty()->toArray();
if($conf){
$value = $conf['value'];
$formType = $conf['form_type'];
if($formType == 'array') return json_decode($value, JSON_UNESCAPED_UNICODE);
if($formType == 'upload'){
$fileInfo = UploadDomain::multiJoin($value);
$return[$key] = $value;
if($fileInfo){
$return[$key.'_path'] = $fileInfo['path'];
$return[$key.'_filename'] = $fileInfo['filename'];
}else{
$return[$key.'_path'] = '';
$return[$key.'_filename'] = '';
}
return $return;
}
return $value ? $value : $defaultValue;
}else{
return $defaultValue ? $defaultValue : '';
}
}
/**
* 通过一个完整的key,设置配置信息
* @param $wholeKey
* @param $value
* @return bool
*/
public function set($wholeKey, $value)
{
if(is_array($value)){
$value = json_encode($value, JSON_UNESCAPED_UNICODE);
}
list($group, $key) = $this->getGroupKey($wholeKey);
$id = \app\model\Conf::where(['group'=>$group, 'key'=>$key])->value('id');
if($id){
\app\model\Conf::where('id', '=', $id)->save(['group' => $group, 'key' => $key]);
}else{
\app\model\Conf::insert(['group' => $group, 'key' => $key, 'value' => $value]);
}
if($this->hasRedis()){
$redis = Cache::store('redis')->handler();
$redis->set($wholeKey, $value);
}
return true;
}
public function del($group, $key)
{
\app\model\Conf::where(['group' => $group, 'key' => $key])->delete();
if($this->hasRedis()){
$redis = Cache::store('redis')->handler();
$redis->del($group . $key);
}
return ;
}
/**
* 通过数组,设置配置信息
* @param $array
* @return bool
*/
public function groupSet($array)
{
foreach($array as $item){
$item['value'] = is_array($item['value']) ? json_encode($item['value'], JSON_UNESCAPED_UNICODE) : $item['value'];
$id = \app\model\Conf::where(['group'=>$item['group'], 'key'=>$item['key']])->value('id');
if($id){
\app\model\Conf::where('id', '=', $id)->save($item);
}else{
\app\model\Conf::create($item);
}
if($this->hasRedis()){
$redis = Cache::store('redis')->handler();
$hashKey = $item['group'] . '.' . $item['key'];
$redis->hset($hashKey, 'group', $item['group']);
$redis->hset($hashKey, 'key', $item['key']);
$redis->hset($hashKey, 'value', $item['value']);
$redis->hset($hashKey, 'form_type', $item['form_type']);
}
}
return true;
}
/**
* 通过配置分组名称,获取整个分组的信息
* @param $group
* @param $onlyMysql boolean 是否仅从数据库取配置
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function groupGet($group, $onlyMysql=false)
{
$return = [];
$items = [];
if(!$onlyMysql){
if($this->hasRedis()){
$redis = Cache::store('redis')->handler();
$keys = $redis->keys($group.'*');
foreach($keys as $key){
$items[$key] = $redis->hGetAll($key);
}
if(!$items){
$items = \app\model\Conf::where(['group'=>$group])->select()->toArray();
}
}else{
$items = \app\model\Conf::where(['group'=>$group])->select()->toArray();
}
}else{
$items = \app\model\Conf::where(['group'=>$group])->select()->toArray();
}
foreach ($items as $k => $v) {
if ($v['form_type'] === 'array') {
$array = json_decode($v['value'], true);
if(!$array){
$return[$v['key']] = [""=>""];
}else{
$return[$v['key']] =$array;
}
} elseif($v['form_type'] === 'upload') {
$fileInfo = UploadDomain::multiJoin($v['value']);
if($fileInfo){
$return[$v['key']] = $fileInfo['id'];
$return[$v['key'].'_path'] = $fileInfo['path'];
$return[$v['key'].'_filename'] = $fileInfo['filename'];
}else{
$return[$v['key']] = '';
$return[$v['key'].'_path'] = '';
$return[$v['key'].'_filename'] = '';
}
} else {
$return[$v['key']] = $v['value'];
}
}
return $return;
}
/**
* 通过完整的key,获取到分组名称和key值
* @param $wholeKey
* @return array|boolean
*/
protected function getGroupKey($wholeKey)
{
$arr = explode('.', $wholeKey);
if(!$arr || count($arr) == 1){
$this->setError('请输入一个完整的key,一个完整的key必须包含至少一个.号');
return false;
}
$key = $arr[count($arr) - 1];
$group = substr($wholeKey, 0 , strrpos($wholeKey, '.'));
return [$group, $key];
}
}
+22
View File
@@ -0,0 +1,22 @@
<?php
namespace app\service;
use think\Facade;
/**
* 系统配置服务门面
* @package app\service
* @method static mixed get($wholeKey, $defaultValue='') 通过完整的key获取配置信息
* @method static mixed set($wholeKey, $value) 通过完整的key设置一个配置信息
* @method static mixed del($group, $key) 删除某个分组下某个key的配置
* @method static mixed groupGet($group, $onlyMysql=false) 通过分组名称,获取整个分组的配置信息
* @method static mixed groupSet($array) 通过hash数组,设置整个分组的配置信息
*/
class ConfServiceFacade extends Facade
{
protected static function getFacadeClass()
{
return Conf::class;
}
}
+586
View File
@@ -0,0 +1,586 @@
<?php
namespace app\service;
class Telegram
{
private $ret;
private $token;
private static $inlineResults = array();
public function __construct($token = NULL)
{
$this->token = $token;
// parent::__construct();
}
/**
* http_build_query兼容多维数组,返回结果数组仍支持http_build_query函数处理
* @author Zjmainstay https://bugs.php.net/bug.php?id=67477
* @param $data array curl传递参数的数组(原装)
* @return array 可curl传递的格式化数组
*/
function http_build_query_develop($data)
{
if (!is_array($data)) {
return $data;
}
foreach ($data as $key => $val) {
if (is_array($val)) {
foreach ($val as $k => $v) {
if (is_array($v)) {
$data = array_merge($data, http_build_query_develop(array("{$key}[{$k}]" => $v)));
} else {
$data["{$key}[{$k}]"] = $v;
}
}
unset($data[$key]);
}
}
return $data;
}
private function fetch($url, $postdata = null)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
if (!is_null($postdata)) {
curl_setopt($ch, CURLOPT_POSTFIELDS, $this->http_build_query_develop($postdata));
}
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$re = curl_exec($ch);
curl_close($ch);
return $re;
}
public function callMethod($method, $param = array(), $detection = true)
{
/** 访问网页 */
$url = 'https://api.telegram.org/bot' . $this->token . '/' . $method;
$ret = json_decode($this->fetch($url, $param), true);
/** 分析结果 */
if ($ret['ok'] == false && $detection == true) {
if ($ret['error_code'] != 400 && $ret['error_code'] != 403) {
$errorModel = new ErrorModel;
$errorModel->sendError(MASTER, '尝试调用 ' . $method . " 时出现问题,参数表如下:\n" . print_r($param, true) . "\n\n返回结果:\n" . print_r($ret, true));
}
}
/** 返回 */
return $ret;
}
public function getWebhook()
{
$this->ret = $this->callMethod('getWebhookInfo', [], false);
return $this->ret;
}
public function setWebhook($newurl)
{
$this->ret = $this->callMethod('setWebhook', [
'url' => $newurl
], false);
return $this->ret;
}
public function sendMessage($chat_id, $text, $reply_to_message_id = NULL, $reply_markup = array(), $parse_mode = 'HTML', $disable_web_page_preview = false, $disable_notification = false)
{
if (isset($GLOBALS['statistics']['send_total']))
$GLOBALS['statistics']['send_total']++;
foreach (str_split($text, 4096) as $text_i => $text_d) {
$tmp = $this->callMethod('sendMessage', [
'chat_id' => $chat_id,
'text' => $text_d,
'reply_to_message_id' => $reply_to_message_id,
'parse_mode' => $parse_mode,
'reply_markup' => $reply_markup,
'disable_web_page_preview' => $disable_web_page_preview,
'disable_notification' => $disable_notification
]);
if ($text_i == 0) $this->ret = $tmp;
}
return $this->ret['result']['message_id'];
}
public function editMessage($chat_id, $message_id, $text, $reply_markup = array(), $parse_mode = 'HTML')
{
$this->ret = $this->callMethod('editMessageText', [
'chat_id' => $chat_id,
'message_id' => $message_id,
'text' => $text,
'parse_mode' => $parse_mode,
'reply_markup' => $reply_markup
]);
return isset($this->ret['result']['message_id'])?$this->ret['result']['message_id']:0;
}
public function deleteMessage($chat_id, $message_id)
{
$this->ret = $this->callMethod('deleteMessage', get_defined_vars());
return $this->ret;
}
public function kickMember($chat_id, $user_id, $until_date = NULL)
{
$this->ret = $this->callMethod('kickChatMember', [
'chat_id' => $chat_id,
'user_id' => $user_id,
'until_date' => $until_date
]);
return $this->ret;
}
//键盘按钮 KeyboardButton $chat_id sender_chat_id
public function KeyboardButton($chat_id, $text, $Keyboard = array(), $message_id = null, $message_thread_id = null)
{
return $this->sendthreadMessage($chat_id, $text, $message_id, $message_thread_id, $Keyboard);
}
public function sendthreadMessage($chat_id, $text, $reply_to_message_id = NULL, $message_thread_id = NULL, $reply_markup = array(), $parse_mode = 'HTML', $disable_web_page_preview = false, $disable_notification = false)
{
if (isset($GLOBALS['statistics']['send_total']))
$GLOBALS['statistics']['send_total']++;
foreach (str_split($text, 4096) as $text_i => $text_d) {
$tmp = $this->callMethod('sendMessage', [
'chat_id' => $chat_id,
'text' => $text_d,
'message_thread_id' => $message_thread_id,
'reply_to_message_id' => $reply_to_message_id,
'parse_mode' => $parse_mode,
'reply_markup' => $reply_markup,
'disable_web_page_preview' => $disable_web_page_preview,
'disable_notification' => $disable_notification
]);
if ($text_i == 0) $this->ret = $tmp;
}
if ($this->ret['ok'] == false) {
if ($this->ret['description'] == "Bad Request: BUTTON_URL_INVALID") {
//按钮设置错误导致异常
$txt = $text . "\r\n" . "\r\n" . "由于按钮信息设置错误,异常,您可以点击按钮参考设置规则";
$button = json_encode(array(
'inline_keyboard' => array(
array(array(
'text' => '参考按钮设置',
'callback_data' => 'xinshouxiuefrb '
))
)
));
$this->sendMessage($chat_id, $txt, null, $button, 'Markdown', true);
}
//按钮设置问题导致报错
if (strpos($this->ret['description'], "keyboard button")) {
//按钮设置错误导致异常
$txt = $text . "\r\n" . "\r\n" . "由于按钮信息设置错误,异常,您可以点击按钮参考设置规则";
$button = json_encode(array(
'inline_keyboard' => array(
array(array(
'text' => '参考按钮设置',
'callback_data' => 'xinshouxiuefrb '
))
)
));
$this->sendMessage($chat_id, $txt, null, $button, 'Markdown', true);
} elseif (strpos($this->ret['description'], "Bad Request: have no rights to send a message")) {
$cuwucishu = Cache::get("cuwusishuo" . $chat_id);
if ($cuwucishu > 100) {
// $this->db->update('group', ['switch' => 0, 'botid' => $this->botid], ['group' => $chat_id]);
Db::name("group")->where(['group' => $chat_id, 'botid' => $this->botid])->update(['switch' => 0]);
$this->getgroupinfo($chat_id, $this->tokenbase, 1);
} else {
Cache::set("cuwusishuo" . $chat_id, $cuwucishu + 1, 36000);
}
} else {
$txt = "本次请求出错了,错误日志如下" . "\r\n" . "检查下设置的地址是否正确或者是存在多余的空格" . "\r\n" . $this->ret['description'] . "\r\n" . "\r\n" . "实在解决不了 反馈 @kaihebug";
if ($text == "查看群规") {
$this->sendMessage($chat_id, $txt);
}
// file_put_contents("tgsendmassttttt.txt", json_encode($this->ret).$text.$reply_markup.$chat_id.$parse_mode."\r\n", FILE_APPEND);
}
if (isset($this->ret['parameters']['retry_after'])) {
$retry_after = $this->ret['parameters']['retry_after'];
Cache::set("retry_after" . $chat_id, 1, $retry_after);
}
}
//下面还可以添加规则
if (isset($this->ret['result']['message_id'])) {
return $this->ret['result']['message_id'];
} else {
return null;
}
//return $this->ret['result']['message_id'];
}
//强制回复 forcereply
public function forcereply($chat_id, $text, $message_id = null, $message_thread_id = null)
{
$buttonsc = json_encode(
array(
'force_reply' => true,
'input_field_placeholder' => $text,
'selective' => true,
)
);
return $this->sendthreadMessage($chat_id, $text, $message_id, $message_thread_id, $buttonsc);
}
public function sendPhoto($chat_id, $photo, $caption = '', $reply_to_message_id = NULL, $reply_markup = array(), $parse_mode = 'HTML')
{
if (is_array($photo)) {
return $this->sendMediaGroup($chat_id, array_map(function ($p) use ($caption, $parse_mode) {
return [
'type' => 'photo',
'media' => $p,
'caption' => $caption,
'parse_mode' => $parse_mode
];
}, $photo), $reply_to_message_id);
}
if (isset($GLOBALS['statistics']['send_total']))
$GLOBALS['statistics']['send_total']++;
$this->ret = $this->callMethod('sendPhoto', [
'chat_id' => $chat_id,
'photo' => $photo,
'caption' => $caption,
'parse_mode' => $parse_mode,
'reply_to_message_id' => $reply_to_message_id,
'reply_markup' => $reply_markup
]);
return isset($this->ret['result']['message_id'])?$this->ret['result']['message_id']:0;;
}
public function sendDocumentphoto($chat_id, $document, $caption = '', $reply_to_message_id = NULL, $reply_markup = array(), $parse_mode = 'HTML')
{
$url = 'https://api.telegram.org/bot' . $this->token . '/sendPhoto';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
$finfo = finfo_file(finfo_open(FILEINFO_MIME_TYPE), $document);
$cFile = new \CURLFile($document, $finfo);
curl_setopt($ch, CURLOPT_POSTFIELDS, $this->http_build_query_develop([
'chat_id' => $chat_id,
'photo' => $cFile,
'caption' => $caption,
'parse_mode' => $parse_mode,
'reply_to_message_id' => $reply_to_message_id,
'reply_markup' => $reply_markup
]));
$result = curl_exec($ch);
curl_close($ch);
$this->ret = json_decode($result, 1);
$message_id=isset($this->ret['result']['message_id'])?$this->ret['result']['message_id']:"";
if(!empty($message_id)){
unlink($document);
}
//var_dump($this->ret);
return $message_id;
}
public function sendMediaGroup($chat_id, $media, $reply_to_message_id = NULL)
{
if (isset($GLOBALS['statistics']['send_total']))
$GLOBALS['statistics']['send_total']++;
$this->ret = $this->callMethod('sendMediaGroup', [
'chat_id' => $chat_id,
'media' => json_encode($media),
'reply_to_message_id' => $reply_to_message_id
]);
return array_map(function ($m) {
return $m['message_id'];
}, $this->ret['result']);
}
public function sendAudio($chat_id, $audio, $caption = '', $reply_to_message_id = NULL, $reply_markup = array(), $parse_mode = 'HTML', $duration = '', $performer = NULL, $title = NULL, $thumb = '', $disable_notification = false)
{
if (isset($GLOBALS['statistics']['send_total']))
$GLOBALS['statistics']['send_total']++;
$this->ret = $this->callMethod('sendAudio', get_defined_vars());
return $this->ret['result']['message_id'];
}
public function sendDocument($chat_id, $document, $caption = '', $reply_to_message_id = NULL, $reply_markup = array(), $parse_mode = 'HTML', $thumb = '', $disable_notification = false)
{
if (isset($GLOBALS['statistics']['send_total']))
$GLOBALS['statistics']['send_total']++;
$this->ret = $this->callMethod('sendDocument', get_defined_vars());
return $this->ret['result']['message_id'];
}
public function sendVideo($chat_id, $video, $duration = '', $width = '', $height = '', $thumb = '', $caption = NULL, $parse_mode = 'HTML', $supports_streaming = '', $disable_notification = false, $reply_to_message_id = '', $reply_markup = '')
{
if (isset($GLOBALS['statistics']['send_total']))
$GLOBALS['statistics']['send_total']++;
$this->ret = $this->callMethod('sendVideo', get_defined_vars());
return $this->ret['result']['message_id'];
}
public function sendAnimation($chat_id, $animation, $duration = NULL, $width = NULL, $height = NULL, $thumb = NULL, $caption = '', $parse_mode = 'HTML', $disable_notification = NULL, $reply_to_message_id = NULL, $reply_markup = array())
{
if (isset($GLOBALS['statistics']['send_total']))
$GLOBALS['statistics']['send_total']++;
$this->ret = $this->callMethod('sendAnimation', get_defined_vars());
return $this->ret['result']['message_id'];
}
public function getBusinessConnection($business_connection_id)
{
$this->ret = $this->callMethod('getBusinessConnection', get_defined_vars());
return $this->ret['result'];
}
public function sendVoice($chat_id, $voice, $caption = NULL, $parse_mode = 'HTML', $duration = '', $disable_notification = false, $reply_to_message_id = '', $reply_markup = '')
{
if (isset($GLOBALS['statistics']['send_total']))
$GLOBALS['statistics']['send_total']++;
$this->ret = $this->callMethod('sendVoice', get_defined_vars());
return $this->ret['result']['message_id'];
}
public function getUserProfilePhotos($user_id, $offset = '', $limit = '')
{
$this->ret = $this->callMethod('getUserProfilePhotos', get_defined_vars());
return $this->ret['result'];
}
public function unbanChatMember($chat_id, $user_id)
{
$this->ret = $this->callMethod('unbanChatMember', get_defined_vars());
return $this->ret['result'];
}
public function restrictChatMember($chat_id, $user_id, $permissions, $until_date = NULL)
{
$this->ret = $this->callMethod('restrictChatMember', get_defined_vars());
return $this->ret['result'];
}
public function promoteChatMember($chat_id, $user_id, $can_change_info = NULL, $can_post_messages = NULL, $can_edit_messages = NULL, $can_delete_messages = NULL, $can_invite_users = NULL, $can_restrict_members = NULL, $can_pin_messages = NULL, $can_promote_members = NULL)
{
$this->ret = $this->callMethod('promoteChatMember', get_defined_vars());
return $this->ret['result'];
}
public function setChatPermissions($chat_id, $permissions)
{
$this->ret = $this->callMethod('setChatPermissions', get_defined_vars());
return $this->ret['result'];
}
public function exportChatInviteLink($chat_id)
{
$this->ret = $this->callMethod('exportChatInviteLink', get_defined_vars());
return $this->ret['result'];
}
public function setChatPhoto($chat_id, $photo)
{
$this->ret = $this->callMethod('setChatPhoto', get_defined_vars());
return $this->ret['result'];
}
public function deleteChatPhoto($chat_id)
{
$this->ret = $this->callMethod('deleteChatPhoto', get_defined_vars());
return $this->ret['result'];
}
public function setChatTitle($chat_id, $title)
{
$this->ret = $this->callMethod('setChatTitle', get_defined_vars());
return $this->ret['result'];
}
public function setChatDescription($chat_id, $description = NULL)
{
$this->ret = $this->callMethod('setChatDescription', get_defined_vars());
return $this->ret['result'];
}
public function pinChatMessage($chat_id, $message_id, $disable_notification = false)
{
$this->ret = $this->callMethod('pinChatMessage', get_defined_vars());
return $this->ret['result'];
}
public function unpinChatMessage($chat_id)
{
$this->ret = $this->callMethod('unpinChatMessage', get_defined_vars());
return $this->ret['result'];
}
public function leaveChat($chat_id)
{
$this->ret = $this->callMethod('leaveChat', get_defined_vars());
return $this->ret['result'];
}
public function getChat($chat_id)
{
$this->ret = $this->callMethod('getChat', get_defined_vars());
return $this->ret['result'];
}
public function getChatMembersCount($chat_id)
{
$this->ret = $this->callMethod('getChatMembersCount', get_defined_vars());
return $this->ret['result'];
}
public function getChatMember($chat_id, $user_id)
{
$this->ret = $this->callMethod('getChatMember', get_defined_vars());
return $this->ret['result'];
}
public function editMessageText($chat_id = '', $message_id = '', $inline_message_id = NULL, $text, $parse_mode = 'HTML', $disable_web_page_preview = '', $reply_markup = '')
{
$this->ret = $this->callMethod('editMessageText', get_defined_vars());
return $this->ret['result']['message_id'];
}
public function editMessageCaption($chat_id = '', $message_id = '', $inline_message_id = NULL, $caption = NULL, $parse_mode = 'HTML', $reply_markup = '')
{
$this->ret = $this->callMethod('editMessageCaption', get_defined_vars());
return $this->ret['result']['message_id'];
}
public function editMessageMedia($chat_id = '', $message_id = '', $inline_message_id = NULL, $media, $reply_markup = '')
{
$this->ret = $this->callMethod('editMessageMedia', get_defined_vars());
return $this->ret['result']['message_id'];
}
public function editMessageReplyMarkup($chat_id = '', $message_id = '', $inline_message_id = NULL, $reply_markup = '')
{
$this->ret = $this->callMethod('editMessageReplyMarkup', get_defined_vars());
return $this->ret['result']['message_id'];
}
public function sendSticker($chat_id, $sticker, $reply_to_message_id = NULL, $reply_markup = array(), $disable_notification = false)
{
if (isset($GLOBALS['statistics']['send_total']))
$GLOBALS['statistics']['send_total']++;
$this->ret = $this->callMethod('sendSticker', get_defined_vars());
return $this->ret['result']['message_id'];
}
public function sendGame($chat_id, $game_name, $reply_to_message_id = NULL, $reply_markup = array())
{
if (isset($GLOBALS['statistics']['send_total']))
$GLOBALS['statistics']['send_total']++;
$this->ret = $this->callMethod('sendGame', [
'chat_id' => $chat_id,
'game_short_name' => $game_name,
'reply_to_message_id' => $reply_to_message_id,
'reply_markup' => $reply_markup
]);
return $this->ret['result']['message_id'];
}
public function setGameScore($user_id, $score, $force = false, $disable_edit_message = false, $chat_id = NULL, $message_id = NULL, $inline_id = NULL)
{
$this->ret = $this->callMethod('setGameScore', [
'user_id' => $user_id,
'score' => $score,
'force' => $force,
'disable_edit_message' => $disable_edit_message,
'chat_id' => $chat_id,
'message_id' => $message_id,
'inline_message_id' => $inline_id
]);
return $this->ret['result']['message_id'];
}
public function forwardMessage($chat_id, $from_chat_id, $message_id, $disable_notification = false)
{
if (isset($GLOBALS['statistics']['send_total']))
$GLOBALS['statistics']['send_total']++;
$this->ret = $this->callMethod('forwardMessage', get_defined_vars());
return $this->ret['result']['message_id'];
}
public function answerCallback($callback_id, $text = '', $show_alert = false, $url = '', $cache_time = 0)
{
if (isset($GLOBALS['statistics']['send_total']))
$GLOBALS['statistics']['send_total']++;
$this->ret = $this->callMethod('answerCallbackQuery', [
'callback_query_id' => $callback_id,
'text' => $text,
'show_alert' => $show_alert,
'url' => $url,
'cache_time' => $cache_time
]);
return $this->ret;
}
public function sendInlineQuery($results)
{
self::$inlineResults = array_merge(self::$inlineResults, $results);
}
public function sendInline($inline_id, $cache_time = 600, $offset = '', $switch_pm_parameter = '')
{
$this->ret = $this->callMethod('answerInlineQuery', [
'inline_query_id' => $inline_id,
'results' => json_encode(self::$inlineResults),
'cache_time' => $cache_time,
'next_offset' => $offset,
'switch_pm_parameter' => $switch_pm_parameter
]);
return $this->ret;
}
public function sendChatAction($chat_id, $action)
{
$this->ret = $this->callMethod('sendChatAction', [
'chat_id' => $chat_id,
'action' => $action
]);
return $this->ret;
}
public function getFile($file_id)
{
$this->ret = $this->callMethod('getFile', [
'file_id' => $file_id,
]);
if ($this->ret['ok']) {
$fileUrl = 'https://api.telegram.org/file/bot' . $this->token . '/' . $this->ret['result']['file_path'];
$this->ret['result']['down_url'] = $fileUrl;
}
return $this->ret;
}
public function getInlineId()
{
return hash('sha256', uniqid(mt_rand(), true));
}
public function getChatAdmin($chat_id)
{
$this->ret = $this->callMethod('getChatAdministrators', [
'chat_id' => $chat_id
]);
return $this->ret['result'];
}
public function getMe()
{
$this->ret = $this->callMethod('getMe', []);
return $this->ret;
}
public function getStickerSet($name)
{
$this->ret = $this->callMethod('getStickerSet', [
'name' => $name
]);
return $this->ret['result'];
}
public function isAdmin($chat_id, $user_id)
{
$ret = false;
$adminList = $this->getChatAdmin($chat_id);
foreach ($adminList as $adminList_d) {
if ($adminList_d['user']['id'] == $user_id) {
$ret = true;
break;
}
}
return $ret;
}
public function atById($userid, $username)
{
return '<a href="tg://user?id=' . $userid . '">@' . $username . '</a>';
}
public function getMaster()
{
return MASTER;
}
public function getBotName()
{
return BOTNAME;
}
public function getReturn()
{
return $this->ret;
}
public function error()
{
$this->callMethod('sendMessage');
}
}
+157
View File
@@ -0,0 +1,157 @@
<?php
namespace app\service\admin;
use app\model\admin\Role;
use app\model\admin\role\Menu;
use laytp\traits\Error;
use think\facade\Request;
/**
* 后台权限服务实现者
* Class Auth
* @package app\service\admin
*/
class Auth
{
use Error;
protected $_noNeedLogin = [];//无需登录的方法名数组
protected $_noNeedAuth = [];//无需鉴权的方法名数组
/**
* 设置无需登录的方法名数组
* @param array $noNeedLogin
*/
public function setNoNeedLogin($noNeedLogin = [])
{
$this->_noNeedLogin = $noNeedLogin;
}
/**
* 获取无需登录的方法名数组
* @return array
*/
public function getNoNeedLogin()
{
return $this->_noNeedLogin;
}
/**
* 当前节点是否需要登录
* @param bool $noNeedLogin
* @return bool true:需要登录,false:不需要登录
*/
public function needLogin($noNeedLogin = false)
{
$noNeedLogin === false && $noNeedLogin = $this->getNoNeedLogin();
$noNeedLogin = is_array($noNeedLogin) ? $noNeedLogin : explode(',', $noNeedLogin);
//为空表示所有方法都需要登录,返回true
if (!$noNeedLogin) {
return true;
}
$noNeedLogin = array_map('strtolower', $noNeedLogin);
$request = Request::instance();
//判断当前请求的操作名是否存在于不需要登录的方法名数组中,如果存在,表明不需要登录,返回false
if (in_array(strtolower($request->action()), $noNeedLogin) || in_array('*', $noNeedLogin)) {
return false;
}
//默认为需要登录
return true;
}
/**
* 设置无需鉴权的方法名数组
* @param array $noNeedAuth
*/
public function setNoNeedAuth($noNeedAuth = [])
{
$this->_noNeedAuth = $noNeedAuth;
}
/**
* 获取无需鉴权的方法名数组
* @return array
*/
public function getNoNeedAuth()
{
return $this->_noNeedAuth;
}
/**
* 当前节点是否需要鉴权
* @param bool $noNeedAuth
* @return bool true:需要登录,false:不需要登录
*/
public function needAuth($noNeedAuth = false)
{
$noNeedAuth === false && $noNeedAuth = $this->getNoNeedAuth();
$noNeedAuth = is_array($noNeedAuth) ? $noNeedAuth : explode(',', $noNeedAuth);
//为空表示所有方法都需要鉴权,返回true
if (!$noNeedAuth) {
return true;
}
$noNeedAuth = array_map('strtolower', $noNeedAuth);
//判断当前请求的操作名是否存在于不需要鉴权的方法名数组中,如果存在,表明不需要鉴权,返回false
if (in_array(strtolower(Request::action()), $noNeedAuth) || in_array('*', $noNeedAuth)) {
return false;
}
//默认为需要鉴权
return true;
}
/**
* 获取某用户拥有的权限列表
* @param $userId int 用户ID,当为空时,为获取当前登录用户权限列表
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getAuthList($userId = 0)
{
$where[] = ['is_show', '=', 1];
$user = $userId ? \app\model\admin\User::findOrEmpty($userId) : UserServiceFacade::getUser();
//当前后台管理员如果是超级管理员,则拥有所有的权限列表
if ($user->is_super_manager === 1) {
$result = \app\model\admin\Menu::where($where)->select()->toArray();
} else {
//如果不是超级管理员,先查询拥有哪些角色,通过角色查询出权限节点列表
$adminUserId = $user->id;
$roleIds = \app\model\admin\role\User::where('admin_user_id', '=', $adminUserId)->column('admin_role_id');
$menuIds = \app\model\admin\menu\Role::where('admin_role_id', 'in', $roleIds)->column('admin_menu_id');
$where[] = ['id', 'in', $menuIds];
$result = \app\model\admin\Menu::where($where)->select()->toArray();
}
return $result;
}
/**
* 获取某用户是否有某节点的权限
* @param integer $userId 登录用户ID
* @param string $node 节点字符串
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function hasAuth($userId, $node)
{
if (!$userId || !$node) return false;
$authList = $this->getAuthList($userId);
$authArr = [];
foreach ($authList as $k => $v) {
$authArr[] = trim($v['rule'], '/');
}
$authArr = array_filter(array_unique($authArr));
sort($authArr);
if (in_array($node, $authArr)) {
return true;
} else {
return false;
}
}
}
+23
View File
@@ -0,0 +1,23 @@
<?php
namespace app\service\admin;
use think\Facade;
/**
* 后台权限服务门面
* @package app\service\admin
* @method static mixed setNoNeedLogin($noNeedLogin) 设置不需要登录的方法名数组
* @method static mixed setNoNeedAuth($noNeedAuth) 设置不需要鉴权的方法名数组
* @method static mixed needLogin() 当前节点是否需要登录
* @method static mixed needAuth() 当前节点是否需要鉴权
* @method static mixed getAuthList($userId) 获取某后台管理员拥有的权限列表
* @method static mixed hasAuth($userId, $node) 某用户是否拥有某个节点的权限
*/
class AuthServiceFacade extends Facade
{
protected static function getFacadeClass()
{
return Auth::class;
}
}
+403
View File
@@ -0,0 +1,403 @@
<?php
namespace app\service\admin;
use app\model\admin\Menu;
use laytp\library\DirFile;
use laytp\library\Http;
use laytp\traits\Error;
use app\model\Migrations;
use think\facade\Config;
use think\facade\Filesystem;
/**
* 插件市场服务实现者
* Class Auth
* @package app\service\admin
*/
class Plugins
{
use Error;
protected $ids=[];
// 离线安装
public function offLineInstall()
{
if (!class_exists('ZipArchive')) {
$this->setError('PHP扩展ZipArchive没有正确安装');
return false;
}
$file = request()->file('laytpUploadFile'); // 获取上传的文件
if (!$file) {
$this->setError('上传失败,请选择需要上传的文件');
return false;
}
$fileExt = strtolower($file->getOriginalExtension());
if($fileExt != 'zip'){
$this->setError('仅允许上传zip文件');
return false;
}
$fileName = $file->getOriginalName();
$pathinfo = pathinfo($fileName);
$plugin = $pathinfo['filename'];
try{
// 将文件上传到指定目录
Filesystem::disk('local')->putFileAs('plugins', $file, $fileName );
$this->insideInstall($plugin);
}catch (\Exception $e){
$this->setError($e->getMessage());
return false;
}
return true;
}
// 安装
public function install($plugin, $laytpGwToken)
{
if (!class_exists('ZipArchive')) {
$this->setError('PHP扩展ZipArchive没有正确安装');
return false;
}
$download = $this->download($plugin, $laytpGwToken);
if(!$download){
return false;
}
$install = $this->insideInstall($plugin);
if(!$install){
return false;
}
return true;
}
// 卸载
public function unInstall($plugin)
{
$pluginDir = $this->getPluginPath($plugin) . DS;
// 删除数据库文件
$migrationsFile = DirFile::recurDir($pluginDir . 'database' . DS . 'migrations');
if($migrationsFile){
foreach($migrationsFile as $file){
$baseNameArr = explode('_', $file['baseName']);
$baseNameArr = explode('.', $baseNameArr[1]);
$migration = Migrations::where('migration_name', '=', ucfirst($baseNameArr[0]))->find();
if($migration) $migration->delete();
@unlink(root_path() . 'database' . DS . 'migrations' . DS . $file['baseName']);
}
}
// 删除菜单
$info = $this->getPluginInfo($plugin);
$menuIds = $info['menu_ids'];
if($menuIds){
Menu::destroy(function($query) use ($menuIds){
$query->where('id', 'in', explode(',', $menuIds));
});
}
// 删除public目录下的文件
$this->removePublicFile($plugin);
// 删除插件目录
DirFile::rmDirs($pluginDir);
// 修改系统插件配置文件config/plugin.php
$this->unInstallPluginConf($plugin, $info);
return true;
}
// 获取上传zip文件所在目录
public function getPluginRuntimeDir()
{
$dir = runtime_path() . 'storage' . DS . 'plugins';
DirFile::createDir($dir);
return $dir;
}
// 下载zip文件到本地
public function download($plugin, $laytpGwToken)
{
$ltVersion = request()->param('ltVersion');
$pluginVersion = request()->param('pluginVersion');
$res = Http::post(Config::get('plugin.apiUrl') . "/plugins/install", [
'plugin'=>$plugin,
'ltVersion'=>$ltVersion,
'pluginVersion'=>$pluginVersion,
], array(
CURLOPT_HTTPHEADER => array(
"token: ".$laytpGwToken
),
));
$resArr = json_decode($res, true);
if($resArr['code'] > 0 ){
$this->setError(['msg'=>$resArr['msg'],'code'=>$resArr['code']]);
return false;
}
$url = $resArr['data']['url'];
$zipSteam = Http::get($url, [], [
CURLOPT_CONNECTTIMEOUT => 30,
CURLOPT_TIMEOUT => 30,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_HTTPHEADER => [
'X-REQUESTED-WITH: XMLHttpRequest'
]
]);
$file = $this->getPluginRuntimeDir() . DS . $plugin . '.zip';
if(file_exists($file)){
@unlink($file);
}
if ($write = fopen($file, 'w')) {
fwrite($write, $zipSteam);
fclose($write);
}
return true;
}
// 内部安装过程
public function insideInstall($plugin)
{
try{
// 解压zip文件
$this->unzip($plugin);
// 复制database文件并执行php think migrate:run命令
$this->migrate($plugin);
// 复制静态文件
if(!function_exists('exec')){
$this->setError('php函数exec不允许执行');
return false;
}
$this->copyPublicFile($plugin);
// 生成菜单,同时将新增的菜单id写入info.ini配置文件中,便于卸载时同时删除菜单
$this->createMenu($plugin);
// 修改系统插件配置文件config/plugin.php
$this->installPluginConf($plugin);
return true;
}catch (\Exception $e){
$this->setError(['msg'=>$e->getMessage(). $e->getLine() . $e->getFile(),'code'=>3]);
return false;
}
}
/**
* 解压插件zip文件
* @param $plugin
* @return bool
* @throws \Exception
*/
public function unzip($plugin)
{
$file = $this->getPluginRuntimeDir() . DS . $plugin . '.zip';
$zip = new \ZipArchive;
if ($zip->open($file) !== TRUE) {
@unlink($file);
throw new \Exception('不能打开zip文件');
}
if(strtolower(trim($zip->getNameIndex(0),'/')) == strtolower($plugin)){
$dir = root_path() . 'plugin' . DS;
}else{
$dir = root_path() . 'plugin' . DS . $plugin . DS;
}
if (!$zip->extractTo($dir)) {
$zip->close();
@unlink($file);
throw new \Exception('不能提取zip文件');
}
$zip->close();
@unlink($file);
return true;
}
// 插件代码文件解压后,执行php run:migrate命令,安装数据库文件
public function migrate($plugin)
{
$pluginDir = $this->getPluginPath($plugin) . DS;
if(is_dir($pluginDir . 'database' . DS . 'migrations')){
DirFile::copyDirs($pluginDir . 'database' . DS . 'migrations', root_path() . 'database' . DS . 'migrations');
// 删除数据库migrations表,已经安装过的版本
$list = scandir($pluginDir . 'database' . DS . 'migrations');
$migrationNameArr = [];
foreach ($list as $value) {
$pathinfo = pathinfo($value);
if ($pathinfo['extension'] == 'php') {
$tempArr = explode('_', $pathinfo['filename']);
$migrationNameArr[] = ucfirst($tempArr[1]);
}
}
Migrations::where('migration_name', 'in', $migrationNameArr)->delete();
sleep(1);
exec('php ' . app()->getRootPath() . '\think migrate:run');
}
return true;
}
// 获取插件路径
public function getPluginPath($plugin)
{
return app()->getRootPath() . DS . 'plugin' . DS . $plugin;
}
// 获取插件信息
public function getPluginInfo($plugin)
{
$pluginPath = $this->getPluginPath($plugin);
$infoFile = $pluginPath . DS . 'info.ini';
$info = [];
if (is_file($infoFile)) {
$info = parse_ini_file($infoFile, true, INI_SCANNER_TYPED) ?: [];
}
return $info;
}
/**
* 设置插件配置信息
* @param $plugin
* @param $array
* @return bool
*/
public function setPluginInfo($plugin, $array)
{
$pluginPath = $this->getPluginPath($plugin);
$file = $pluginPath . DS . 'info.ini';
if (!isset($array['name'])) {
$this->setError("插件配置写入失败");
return false;
}
$res = array();
foreach ($array as $key => $val) {
if (is_array($val)) {
$res[] = "[$key]";
foreach ($val as $sKey => $sVal)
$res[] = "$sKey = " . (is_numeric($sVal) ? $sVal : $sVal);
} else
$res[] = "$key = " . (is_numeric($val) ? $val : $val);
}
if ($handle = fopen($file, 'w')) {
fwrite($handle, implode("\n", $res) . "\n");
fclose($handle);
} else {
$this->setError("文件没有写入权限");
return false;
}
return true;
}
// 生成插件菜单
public function createMenu($plugin)
{
$menuFile = root_path() . 'plugin' . DS . $plugin . DS . 'menu.php';
if(!is_file($menuFile)){
return true;
}
$menus = include_once $menuFile;
$info = $this->getPluginInfo($plugin);
if(isset($info['parent_menu']) && $info['parent_menu'] === 'first'){
$firstMenuId = Menu::where(['pid' => 0, 'is_show' => 1])->order(['sort'=>'desc', 'id'=>'asc'])->value('id');
$ids = $this->createMenuIds($menus, $firstMenuId);
}else{
$ids = $this->createMenuIds($menus, 0);
}
$info['menu_ids'] = implode(',', $ids);
if(!$this->setPluginInfo($plugin, $info)) return false;
return true;
}
public function createMenuIds($menus, $pid=0)
{
foreach($menus as $menu){
$id = Menu::insertGetId([
'name' => $menu['name'],
'des' => isset($menu['des']) ? $menu['des'] : '',
'href' => isset($menu['href']) ? $menu['href'] : '',
'rule' => isset($menu['rule']) ? $menu['rule'] : '',
'is_menu' => $menu['is_menu'],
'pid' => $pid,
'icon' => isset($menu['icon']) ? $menu['icon'] : ''
]);
$this->ids[] = $id;
if(isset($menu['children'])){
self::createMenuIds($menu['children'],$id);
}
}
return $this->ids;
}
// 复制静态文件,包括html css js
public function copyPublicFile($plugin)
{
$pluginDir = $this->getPluginPath($plugin) . DS;
if(is_dir($pluginDir . 'public')){
DirFile::copyDirs($pluginDir . 'public', root_path() . 'public');
}
return true;
}
// 删除静态文件,包括html css js
public function removePublicFile($plugin)
{
$pluginDir = $this->getPluginPath($plugin) . DS;
$pluginHtmlDir = $pluginDir . 'public' . DS . 'admin' . DS . 'plugin' . DS . $plugin;
$publicHtmlDir = root_path() . 'public' . DS . 'admin' . DS . 'plugin' . DS . $plugin;
if(is_dir($pluginHtmlDir) && $publicHtmlDir){
DirFile::rmDirs($publicHtmlDir);
}
$pluginStaticDir = $pluginDir . 'public' . DS . 'static' . DS . 'plugin' . DS . $plugin;
$publicStaticDir = root_path() . 'public' . DS . 'static' . DS . 'plugin' . DS . $plugin;
if(is_dir($pluginStaticDir) && $publicStaticDir){
DirFile::rmDirs($publicStaticDir);
}
return true;
}
// 重新生成config/plugin.php文件
public function installPluginConf($plugin)
{
$pluginConf = Config::get('plugin');
$pluginConf['installed'][] = $plugin;
$pluginConf['installed'] = array_unique($pluginConf['installed']);
sort($pluginConf['installed']);
$info = $this->getPluginInfo($plugin);
if(isset($info['is_editor']) && $info['is_editor']){
$pluginConf['installedEditor'][] = $plugin;
$pluginConf['installedEditor'] = array_unique($pluginConf['installedEditor']);
sort($pluginConf['installedEditor']);
}
$fileName = root_path() . DS . 'config' . DS . 'plugin.php';
file_put_contents($fileName,"<?php\nreturn " . var_export($pluginConf,true) . ';');
return true;
}
// 重新生成config/plugin.php文件
public function unInstallPluginConf($plugin, $info)
{
$pluginConf = Config::get('plugin');
foreach($pluginConf['installed'] as $k=>$installed){
if($installed === $plugin) unset($pluginConf['installed'][$k]);
}
$pluginConf['installed'] = array_unique($pluginConf['installed']);
sort($pluginConf['installed']);
if(isset($info['is_editor']) && $info['is_editor']){
foreach($pluginConf['installedEditor'] as $k=>$installedEditor){
if($installedEditor === $plugin) unset($pluginConf['installedEditor'][$k]);
}
$pluginConf['installedEditor'] = array_unique($pluginConf['installedEditor']);
sort($pluginConf['installedEditor']);
}
$fileName = root_path() . DS . 'config' . DS . 'plugin.php';
file_put_contents($fileName,"<?php\nreturn " . var_export($pluginConf,true) . ';');
return true;
}
}
+23
View File
@@ -0,0 +1,23 @@
<?php
namespace app\service\admin;
use think\Facade;
/**
* 插件市场服务门面
* @package app\service\admin
* @method static mixed getError() 获取错误信息
* @method static mixed offLineInstall() 离线安装
* @method static mixed unInstall($plugin) 卸载
* @method static mixed getPluginPath($plugin) 获取插件目录
* @method static mixed getPluginInfo($plugin) 获取插件信息
* @method static mixed install($plugin, $laytpGwToken) 安装插件
*/
class PluginsServiceFacade extends Facade
{
protected static function getFacadeClass()
{
return Plugins::class;
}
}
+130
View File
@@ -0,0 +1,130 @@
<?php
namespace app\service\admin;
use laytp\library\Token;
use laytp\traits\Error;
/**
* 后台用户服务实现者
* @package app\service\admin
*/
class User
{
use Error;
protected $_user = null;//实例化的用户对象
protected $_token = null;//用户登录凭证,token
protected $_isLogin = null;//当前用户是否登录
protected $userModel = null;//用户数据模型
protected $allowFields = ['id', 'username', 'nickname', 'avatar', 'is_super_manager', 'status', 'create_time'];
protected $tokenKeepTime = 365 * 24 * 60 * 60;//Token默认有效时长,单位秒,365天
/**
* 初始化
* @param $token
* @return bool
*/
public function init($token)
{
if (!$token) {
$this->setError('token不能为空,请重新登录');
return false;
}
$data = Token::get($token);
if (!$data) {
$this->setError('token无效,请重新登录');
return false;
}
$userId = intval($data['user_id']);
if ($userId > 0) {
$user = \app\model\admin\User::with(['avatar_file'])->findOrEmpty($userId);
if (!$user) {
$this->setError('账号不存在,请重新登录');
return false;
}
//用户状态 1正常 2禁用
if ($user['status'] != 1) {
$this->setError('账号被禁用,请联系管理员');
return false;
}
$this->_user = $user;
$this->_isLogin = true;
$this->_token = $token;
return true;
} else {
$this->setError('账号不存在,请重新登录');
return false;
}
}
/**
* 退出登录
* @return bool
*/
public function logout()
{
if (!$this->_isLogin) {
$this->setError('你没有登录');
return false;
}
//设置登录标识
$this->_isLogin = false;
//删除Token
Token::delete($this->_token);
return true;
}
/**
* 获取登录用户信息
*/
public function getUserInfo()
{
$data = $this->_user->toArray();
$allowFields = $this->getAllowFields();
$userInfo = array_intersect_key($data, array_flip($allowFields));
$userInfo['avatar_file'] = $data['avatar_file'];
$userInfo = array_merge($userInfo, ['token' => $this->_token]);
return $userInfo;
}
/**
* 获取允许输出的字段
* @return array
*/
public function getAllowFields()
{
return $this->allowFields;
}
/**
* 获取User模型
* @return User
*/
public function getUser()
{
return $this->_user;
}
/**
* 判断是否登录
* @return boolean
*/
public function isLogin()
{
if ($this->_isLogin) {
return true;
}
return false;
}
/**
* 获取当前Token
* @return string
*/
public function getToken()
{
return $this->_token;
}
}
+25
View File
@@ -0,0 +1,25 @@
<?php
namespace app\service\admin;
use think\Facade;
/**
* 后台用户服务门面
* @package app\service\admin
* @method static mixed init($token) 初始化
* @method static mixed getError() 获取错误信息
* @method static mixed logout() 退出登录
* @method static mixed getUserInfo() 获取登录用户信息
* @method static mixed getAllowFields() 获取允许展示的字段
* @method static mixed getUser() 获取User模型
* @method static mixed isLogin() 获取登录状态
* @method static mixed getToken() 获取token
*/
class UserServiceFacade extends Facade
{
protected static function getFacadeClass()
{
return User::class;
}
}
+44
View File
@@ -0,0 +1,44 @@
- 容器
- 通俗一点理解容器,我们用PHP中唯一的数据结构数组来做比方。
- 你可以直接把容器理解为一个数组
- 数组中的key是一个字符串,作为容器中类实例的标识,方便使用这个标识来调用类的实例
- 数组中的value就是一个一个类的实例
- 容器就是存储具有key-value数据结构的,用于存储标识和类实例的一个大集合
- tp6中,默认在容器中添加了很多的类实例,手册中的[系统内置绑定到容器中的类库包括]的表格中说明了标识和系统类库对应的关系
- 依赖注入
- 在容器中存入了很多key-value形式的类实例,就可以实现依赖注入。
- 比如在控制器的某个方法中,使用依赖注入的方式,参数设置一个类,其实这个类在框架进行初始化时,就已经放在容器中了
- 如果容器中不存在,容器也可以使用composer的类自动加载机制,实时的将类注入进入容器
- 服务
- 平时在编写程序的时候,我们会写很多服务,比如权限验证,用户信息,支付,手机短信,邮件等等
- 在tp6之前,我们一般就是写一个一个的类,需要用到的时候就new一下,得到服务类的实例,然后调用服务类的方法
- 现在tp6使用容器来管理所有的类,当服务类需要使用依赖注入的方式调用时,就需要把服务类注册到容器中
- tp6就提供了一个配置文件,service.php,写上哪些类需要注册到容器中
- 注册到容器中的类还可以设置一个标识,那就使用服务类的register方法,或者bind方法
- 服务不推荐使用,因为php本身语言的问题,容器的初始化是每次请求都会执行的,意味着,每次请求都会实例化所有服务类并注册进容器。服务越多,初始化消耗越大
- 门面
- 门面为容器中的动态类提供了一个静态调用接口,相比于传统的静态方法调用,带来了更好的可测试性和扩展性,你可以为任何的非静态类库定义一个facade类。
laytp里面的服务,其实就是用了tp6的门面,一般由两个文件组成
- 服务具体实现者 这个类无需继承任何基类,只需要实现服务的具体方法
- 服务门面
- 编写服务门面的原因要谈到如何调用服务
- 第一种:使用new关键字,创建一个服务具体实现者的类实例,就可以调用服务的方法,这种方法比较通用,但是需要多一行new的代码,而且服务本身还要考虑是否需要实现单例
- 第二种,使用依赖注入。在控制器层可以使用此方法,调用起来也很方便,但是中间件中,不能使用依赖注入
- 第三种,编写服务门面类,可以直接使用静态方法调用服务实现者的具体实现方法。此方式代码编写方便,中间件中也可以使用,而且服务门面基类实现了单例
- 唯一要注意的是,服务门面类需要写好注释,PHPStorm编辑器的代码跟踪才能跟踪到门面类里面来,然后就可以在门面的getFacadeClass方法中继续跟踪服务的具体实现者类
- 服务的目录 /app/service
- 命名规范,以ServiceFacade结尾的就是服务门面
- 服务提供者,文件名和类名一般以Service结尾
- 举例:
- tp6的验证码就是使用的服务提供者来提供全局调用的
- vendor/topthink/think-captcha/src/CaptchaService.php
- 提供者仅需实现服务注册方法,如果服务需要做一些其他操作,可以使用服务启动方法进行操作。
- 在提供者中,服务启动方法,可以进行路由注册、验证器扩展标识,
- 服务提供者的用处
- 提供者实现了服务注册方法,就能进行依赖注入的方式调用服务
- 提供者的启动方法,服务在调用之前,就会执行的一些程序
+59
View File
@@ -0,0 +1,59 @@
<?php
namespace app\service\api;
use laytp\traits\Error;
use think\facade\Request;
/**
* Api权限服务实现者
* Class Auth
* @package app\api\service
*/
class Auth
{
use Error;
protected $_noNeedLogin = [];//无需登录的方法名数组
/**
* 设置无需登录的方法名数组
* @param array $noNeedLogin
*/
public function setNoNeedLogin($noNeedLogin = [])
{
$this->_noNeedLogin = $noNeedLogin;
}
/**
* 获取无需登录的方法名数组
* @return array
*/
public function getNoNeedLogin()
{
return $this->_noNeedLogin;
}
/**
* 当前节点是否需要登录
* @param bool $noNeedLogin
* @return bool true:需要登录,false:不需要登录
*/
public function needLogin($noNeedLogin = false)
{
$noNeedLogin === false && $noNeedLogin = $this->getNoNeedLogin();
$noNeedLogin = is_array($noNeedLogin) ? $noNeedLogin : explode(',', $noNeedLogin);
//为空表示所有方法都需要登录,返回true
if (!$noNeedLogin) {
return true;
}
$noNeedLogin = array_map('strtolower', $noNeedLogin);
$request = Request::instance();
//判断当前请求的操作名是否存在于不需要登录的方法名数组中,如果存在,表明不需要登录,返回false
if (in_array(strtolower($request->action()), $noNeedLogin) || in_array('*', $noNeedLogin)) {
return false;
}
//默认为需要登录
return true;
}
}
+20
View File
@@ -0,0 +1,20 @@
<?php
namespace app\service\api;
use think\Facade;
/**
* Api权限服务门面
* @package app\api\service
* @method static mixed setNoNeedLogin($noNeedLogin) 设置不需要登录的方法名数组
* @method static mixed getNoNeedLogin() 获取无需登录的方法名数组
* @method static mixed needLogin() 获取当前节点是否需要登录
*/
class AuthServiceFacade extends Facade
{
protected static function getFacadeClass()
{
return Auth::class;
}
}
+80
View File
@@ -0,0 +1,80 @@
<?php
namespace app\service\api;
use app\service\ConfServiceFacade;
use laytp\traits\Error;
use think\facade\Config;
use think\facade\Env;
use think\facade\Request;
/**
* Api验证签名服务实现者
* Class CheckSign
* @package app\api\service
*/
class CheckSign
{
use Error;
protected $_noNeedCheckSign = [];//无需验证签名的方法名数组
/**
* 设置无需验证签名的方法名数组
* @param array $noNeedCheckSign
*/
public function setNoNeedCheckSign($noNeedCheckSign = [])
{
$this->_noNeedCheckSign = $noNeedCheckSign;
}
/**
* 获取无需验证签名的方法名数组
* @return array
*/
public function getNoNeedCheckSign()
{
return $this->_noNeedCheckSign;
}
/**
* 当前节点是否需要验证签名
* @param bool $noNeedCheckSign
* @return bool true:需要验证签名,false:不需要验证签名
*/
public function needCheckSign($noNeedCheckSign = false)
{
$noNeedCheckSign === false && $noNeedCheckSign = $this->getNoNeedCheckSign();
$noNeedCheckSign = is_array($noNeedCheckSign) ? $noNeedCheckSign : explode(',', $noNeedCheckSign);
//为空表示所有方法都需要验证签名,返回true
if (!$noNeedCheckSign) {
return true;
}
$noNeedCheckSign = array_map('strtolower', $noNeedCheckSign);
$request = Request::instance();
//判断当前请求的操作名是否存在于不需要验证签名的方法名数组中,如果存在,表明不需要验证签名,返回false
if (in_array(strtolower($request->action()), $noNeedCheckSign) || in_array('*', $noNeedCheckSign)) {
return false;
}
//默认为需要验证签名
return true;
}
/**
* 验证签名
*/
public function check()
{
$request = Request::instance();
$requestTime = $request->header('request-time');
$sign = $request->header('sign');
$signKey = ConfServiceFacade::get('system.basic.signKey');
$backendSign = strtoupper(md5(md5($requestTime).md5($signKey)));
if($sign != $backendSign){
$this->setError($backendSign);
return false;
}
return true;
}
}
+22
View File
@@ -0,0 +1,22 @@
<?php
namespace app\service\api;
use think\Facade;
/**
* Api验证签名服务门面
* @package app\api\service
* @method static mixed setNoNeedCheckSign($noNeedCheckSign) 设置不需要验证签名的方法名数组
* @method static mixed getNoNeedCheckSign() 获取无需验证签名的方法名数组
* @method static mixed needCheckSign() 获取当前节点是否需要验证签名
* @method static mixed check() 验证签名
* @method static mixed getError() 获取错误信息
*/
class CheckSignServiceFacade extends Facade
{
protected static function getFacadeClass()
{
return CheckSign::class;
}
}
+178
View File
@@ -0,0 +1,178 @@
<?php
namespace app\service\api;
use laytp\library\Str;
use laytp\library\Token;
use laytp\traits\Error;
use laytp\library\Random;
/**
* Api用户服务实现者
* @package app\api\service
*/
class Member
{
use Error;
protected $_user = null;//实例化的用户对象
protected $_token = null;//用户登录凭证,token
protected $_isLogin = null;//当前用户是否登录
protected $userModel = null;//用户数据模型
protected $allowFields = ['id', 'email', 'nickname', 'avatar'];
protected $tokenKeepTime = 10 * 365 * 24 * 60 * 60;//Token默认有效时长,单位秒,365天
/**
* 初始化
* @param $token
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function init($token)
{
if (!$token) {
$this->setError('token不能为空,请重新登录');
return false;
}
$data = Token::get($token);
if (!$data) {
$this->setError('token无效,请重新登录');
return false;
}
$userId = intval($data['user_id']);
if ($userId > 0) {
$user = \app\model\Member::find($userId);
if (!$user) {
$this->setError('账号不存在,请重新登录');
return false;
}
//用户状态 1正常 2锁定
if ($user['status'] != 1) {
$this->setError('账号被锁定,请联系管理员');
return false;
}
$this->_user = $user;
$this->_isLogin = true;
$this->_token = $token;
return true;
} else {
$this->setError('账号不存在,请重新登录');
return false;
}
}
/**
* 邮箱+密码注册登录
* @param $params
* @return bool
*/
public function emailRegLogin($params)
{
try {
$user = \app\model\Member::where('email', '=', $params['email'])->find();
if (!$user) {
$data = [
'email' => $params['email'],
'password' => Str::createPassword($params['password']),
'status' => 1,
'login_time' => date('Y-m-d H:i:s'),
'login_ip' => request()->ip(),
];
$user = \app\model\Member::create($data);
$this->_user = \app\model\Member::find($user->id);
} else {
$this->_user = $user;
}
//设置Token
$this->_token = Random::uuid();
Token::set($this->_token, $user->id, $this->tokenKeepTime);
return true;
} catch (\Exception $e) {
$this->setError('操作异常');
return false;
}
}
/**
* 退出登录
*/
public function logout()
{
if (!$this->_isLogin) {
$this->setError('你没有登录');
return false;
}
//设置登录标识
$this->_isLogin = false;
//删除Token
Token::delete($this->_token);
return true;
}
/**
* 兼容调用user模型的属性
*
* @param string $name
* @return mixed
*/
public function __get($name)
{
return $this->_user ? $this->_user->$name : null;
}
/**
* 获取登录用户信息
*/
public function getUserInfo()
{
$data = $this->_user->toArray();
$allowFields = $this->getAllowFields();
$userInfo = array_intersect_key($data, array_flip($allowFields));
$userInfo = array_merge($userInfo, ['token' => $this->_token]);
return $userInfo;
}
/**
* 获取允许输出的字段
* @return array
*/
public function getAllowFields()
{
return $this->allowFields;
}
/**
* 获取User模型
* @return Member
*/
public function getUser()
{
return $this->_user;
}
/**
* 判断是否登录
* @return boolean
*/
public function isLogin()
{
if ($this->_isLogin) {
return true;
}
return false;
}
/**
* 获取当前Token
* @return string
*/
public function getToken()
{
return $this->_token;
}
}
+26
View File
@@ -0,0 +1,26 @@
<?php
namespace app\service\api;
use think\Facade;
/**
* Api用户服务门面
* @package plugin\core\service
* @method static mixed init($token) 初始化
* @method static mixed getError() 获取错误信息
* @method static mixed emailRegLogin($param) 邮箱密码注册登录
* @method static mixed logout() 退出登录
* @method static mixed getUserInfo() 获取登录用户信息
* @method static mixed getUser() 获取User模型
* @method static mixed isLogin() 获取登录状态
* @method static mixed getToken() 获取token
* @method static mixed getAllowFields() 允许输出的字段
*/
class MemberServiceFacade extends Facade
{
protected static function getFacadeClass()
{
return Member::class;
}
}
+9
View File
@@ -0,0 +1,9 @@
这里面写插件提供的服务
一个服务,一般由三个文件组成
- 服务具体实现者 这个类无需继承任何基类,只需要实现服务的具体方法
- 服务提供者,也可以理解为服务注册者,将服务实现者类注册进ThinkPHP容器中,方便进行调用
- 服务门面,为了在某些地方,不方便直接使用服务提供者的地方,能静态的访问服务实现者的方法,比如中间件中需要使用服务,那么就可能需要使用到服务门面
其中,以Service.php结尾的文件为服务提供者,服务提供者的类名全称会加入/config/service.php文件中,完成服务的注册
不移动到/app/common下了,负载均衡就全量复制到多个服务器
+2759
View File
File diff suppressed because it is too large Load Diff
+20
View File
@@ -0,0 +1,20 @@
<?php
namespace app\validate\admin\bot;
use think\Validate;
class Add extends Validate
{
//数组顺序就是检测的顺序
protected $rule = [
'password' => 'confirm:re_password|min:6|max:26',
];
//定义内置方法检验失败后返回的字符
protected $message = [
'password.confirm' => '两次密码输入不相同',
'password.min' => '密码长度不能低于6个字符',
'password.max' => '密码长度不能高于26个字符',
];
}
+20
View File
@@ -0,0 +1,20 @@
<?php
namespace app\validate\admin\bot;
use think\Validate;
class Edit extends Validate
{
//数组顺序就是检测的顺序
protected $rule = [
'password' => 'confirm:re_password|min:6|max:26',
];
//定义内置方法检验失败后返回的字符
protected $message = [
'password.confirm' => '两次密码输入不相同',
'password.min' => '密码长度不能低于6个字符',
'password.max' => '密码长度不能高于26个字符',
];
}
+20
View File
@@ -0,0 +1,20 @@
<?php
namespace app\validate\admin\member;
use think\Validate;
class Add extends Validate
{
//数组顺序就是检测的顺序
protected $rule = [
'password' => 'confirm:re_password|min:6|max:26',
];
//定义内置方法检验失败后返回的字符
protected $message = [
'password.confirm' => '两次密码输入不相同',
'password.min' => '密码长度不能低于6个字符',
'password.max' => '密码长度不能高于26个字符',
];
}
+20
View File
@@ -0,0 +1,20 @@
<?php
namespace app\validate\admin\member;
use think\Validate;
class Edit extends Validate
{
//数组顺序就是检测的顺序
protected $rule = [
'password' => 'confirm:re_password|min:6|max:26',
];
//定义内置方法检验失败后返回的字符
protected $message = [
'password.confirm' => '两次密码输入不相同',
'password.min' => '密码长度不能低于6个字符',
'password.max' => '密码长度不能高于26个字符',
];
}
+49
View File
@@ -0,0 +1,49 @@
<?php
namespace app\validate\admin\user;
use app\model\admin\User;
use think\Validate;
class Add extends Validate
{
//数组顺序就是检测的顺序
protected $rule = [
'username' => 'require|length:2,30|checkUsername:',
'nickname' => 'require|length:2,30|checkNickname:',
'password' => 'require|length:6,30|confirm:re_password',
'is_super_manager' => 'require',
'status' => 'require',
];
//定义内置方法检验失败后返回的字符
protected $message = [
'username.require' => '用户名不能为空',
'username.length' => '用户名长度2-30',
'nickname.require' => '昵称不能为空',
'nickname.length' => '昵称长度2-30',
'password.require' => '密码不能为空',
'password.length' => '密码长度6-30',
'password.confirm' => '两次密码输入不相同',
'is_super_manager.require' => '请设置用户是否为超级管理员',
'status.require' => '请设置账号的状态',
];
//验证用户名的唯一性
protected function checkUsername($username, $rule, $data){
$userId = User::getFieldByUsername($username, 'id');
if($userId){
return '用户名已存在';
}
return true;
}
//验证昵称的唯一性
protected function checkNickname($nickname, $rule, $data){
$userId = User::getFieldByUsername($nickname, 'id');
if($userId){
return '昵称已存在';
}
return true;
}
}
+53
View File
@@ -0,0 +1,53 @@
<?php
namespace app\validate\admin\user;
use app\model\admin\User;
use think\Validate;
class Edit extends Validate
{
//数组顺序就是检测的顺序
protected $rule = [
'id' => 'require|checkId:',
'username' => 'require|length:2,30|checkUsername:',
'nickname' => 'require|length:2,30',
'password' => 'length:6,30|confirm:re_password',
'is_super_manager' => 'require',
'status' => 'require',
];
//定义内置方法检验失败后返回的字符
protected $message = [
'username.require' => '用户名不能为空',
'username.length' => '用户名长度2-30',
'nickname.require' => '昵称不能为空',
'nickname.length' => '昵称长度2-30',
'password.length' => '密码长度6-30',
'password.confirm' => '两次密码输入不相同',
'is_super_manager.require' => '请设置用户是否为超级管理员',
'status.require' => '请设置账号的状态',
];
public function checkId($id, $rule, $data)
{
if ($id == 1) {
if ($data['is_super_manager'] == 2) {
return '不允许将初始化用户编辑成非超级管理员';
}
if ($data['status'] == 2) {
return '不允许禁用初始化用户';
}
}
return true;
}
//验证用户名的唯一性
protected function checkUsername($username, $rule, $data){
$userId = User::getFieldByUsername($username,'id');
if($userId && $userId != $data['id']){
return '用户名已存在';
}
return true;
}
}
+60
View File
@@ -0,0 +1,60 @@
<?php
namespace app\validate\admin\user;
use app\model\admin\User;
use app\service\ConfServiceFacade;
use laytp\library\Str;
use think\facade\Config;
use think\Validate;
class Login extends Validate
{
//数组顺序就是检测的顺序,比如这里,会先检测code验证码的正确性
protected $rule = [
'verify_code' => 'checkVerifyCode:',
'username' => 'require',
'password' => 'require|checkPassword:',
];
//定义内置方法检验失败后返回的字符
protected $message = [
'username.require' => '用户名不能为空',
'password.require' => '密码不能为空',
];
//自定义验证码检验方法
protected function checkVerifyCode($verifyCode)
{
if(ConfServiceFacade::get("system.basic.loginNeedCaptcha") == 1){
if (!captcha_check($verifyCode)) {
return '验证码错误';
} else {
return true;
}
}
return true;
}
//自定义密码检验方法
protected function checkPassword($password, $rule, $data)
{
$username = $data['username'];
$user = User::where('username', '=', $username)->find();
if (!$user) {
return '用户名或密码错误';
}
$passwordHash = $user->password;
if (!Str::checkPassword($password, $passwordHash)) {
return '用户名或密码错误';
}
$status = $user->status;
if ($status == 2) {
return '用户已被禁用,请联系管理员';
}
return true;
}
}
+36
View File
@@ -0,0 +1,36 @@
<?php
namespace app\validate\admin\user;
use app\model\admin\User;
use think\Validate;
class singleEdit extends Validate
{
//数组顺序就是检测的顺序
protected $rule = [
'id' => 'require',
'nickname' => 'require|length:2,30',
'old_password' => 'length:6,30|checkOldPassword:',
'password' => 'length:6,30|confirm:re_password',
];
//定义内置方法检验失败后返回的字符
protected $message = [
'id.require' => 'ID不能为空',
'nickname.require' => '昵称不能为空',
'nickname.length' => '昵称长度2-30',
'old_password.length' => '旧密码长度6-30',
'password.length' => '新密码长度6-30',
'password.confirm' => '两次新密码输入不相同',
];
//验证旧密码是否正确
protected function checkOldPassword($oldPassword, $rule, $data){
$passwordHash = User::getFieldById($data['id'], 'password');
if(!password_verify(md5($oldPassword), $passwordHash)){
return '旧密码错误';
}
return true;
}
}
+34
View File
@@ -0,0 +1,34 @@
<?php
namespace app\validate\api\member;
//邮箱密码登录验证器
use app\model\Member;
use laytp\library\Str;
use think\Validate;
class EmailLogin extends Validate
{
//数组顺序就是检测的顺序,比如这里,会先检测code验证码的正确性
protected $rule = [
'email' => 'require|email',
'password' => 'require|length:6,26|checkPassword:',
];
//定义内置方法检验失败后返回的字符
protected $message = [
'email.require' => '邮箱不能为空',
'email.email' => '邮箱格式不正确',
'password.require' => '密码不能为空',
'password.length' => '密码长度需要6到26个字符',
];
//自定义密码检验方法
protected function checkPassword($password, $rule, $data)
{
$email = $data['email'];
$user = new Member();
$passwordHash = $user->getFieldByEmail($email, 'password');
return (!Str::checkPassword($password, $passwordHash)) ? '账户信息错误' : true;
}
}
+36
View File
@@ -0,0 +1,36 @@
<?php
namespace app\validate\api\member;
//邮箱密码注册验证器
use app\model\Member;
use think\Validate;
class EmailReg extends Validate
{
//数组顺序就是检测的顺序,比如这里,会先检测code验证码的正确性
protected $rule = [
'email' => 'require|email|checkEmail:',
'password' => 'require|length:6,26|confirm:repassword',
'repassword' => 'require|length:6,26',
];
//定义内置方法检验失败后返回的字符
protected $message = [
'email.require' => '邮箱不能为空',
'email.email' => '邮箱格式不正确',
'password.require' => '密码不能为空',
'password.length' => '密码长度需要6到26个字符',
'password.confirm' => '两次密码输入不相同',
'repassword.require' => '重复密码不能为空',
'repassword.length' => '重复密码长度需要6到26个字符',
];
protected function checkEmail($email, $rule, $data){
$exist = Member::getFieldByEmail($email,'id');
if($exist){
return '邮箱已存在';
}
return true;
}
}
Executable
+43
View File
@@ -0,0 +1,43 @@
# 记录使用过的命令
```
#安装composer
curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer
#composer镜像使用阿里云的镜像
composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
#安装tp6.x稳定版
composer create-project topthink/think laytp2.0
#更新tp版本
composer update topthink/framework
#安装tp多应用模式扩展
composer require topthink/think-multi-app
#安装数据库迁移工具
composer require topthink/think-migration
#创建laytp数据库迁移文件
php think migrate:create Laytp
#执行tp数据库迁移文件
php think migrate:run
#创建插入数据库数据文件,用于初始化数据库的数据
php think seed:create Laytp
#执行数据插入
php think seed:run
#安装tp模板引擎,一键生成Api文档需要使用到
composer require topthink/think-view
#验证码库
composer require topthink/think-captcha
#ide助手
composer require topthink/think-ide-helper
##用户安装过程
#安装composer库
composer update
#修改database.php或者.env文件,配置数据库连接
#执行数据库迁移命令,创建数据表,给数据表设置默认数据
php think migrate:run
#配置nginx,以local.laytp2.com域名为例
1.本地hosts增加如下一行
127.0.0.1 local.laytp2.com
2.修改nginx虚拟主机目录,增加local.laytp2.com.conf文件,文件内容如下:
-nginx配置内容
```
Executable
+58
View File
@@ -0,0 +1,58 @@
{
"name": "topthink/think",
"description": "the new thinkphp framework",
"type": "project",
"keywords": [
"framework",
"thinkphp",
"ORM"
],
"homepage": "http://thinkphp.cn/",
"license": "Apache-2.0",
"authors": [
{
"name": "liu21st",
"email": "liu21st@gmail.com"
},
{
"name": "yunwuxin",
"email": "448901948@qq.com"
}
],
"require": {
"php": ">=7.1.0",
"topthink/framework": "^6.0.0",
"topthink/think-orm": "^2.0",
"topthink/think-captcha": "^3.0",
"topthink/think-migration": "^3.0",
"topthink/think-view": "^1.0",
"alibabacloud/sdk": "^1.8",
"aliyuncs/oss-sdk-php": "^2.4",
"qiniu/php-sdk": "^7.4",
"topthink/think-filesystem": "^2.0",
"cpliakas/git-wrapper": "^3.1",
"dh2y/think-qrcode": "^2.0"
},
"require-dev": {
"symfony/var-dumper": "^4.2",
"topthink/think-trace":"^1.0"
},
"autoload": {
"psr-4": {
"app\\": "app",
"plugin\\": "plugin"
},
"psr-0": {
"": "extend/"
}
},
"config": {
"preferred-install": "dist"
},
"scripts": {
"post-autoload-dump": [
"@php think service:discover",
"@php think vendor:publish"
]
}
}
+2
View File
@@ -0,0 +1,2 @@
laytp.php
!.gitignore
Executable
+32
View File
@@ -0,0 +1,32 @@
<?php
// +----------------------------------------------------------------------
// | 应用设置
// +----------------------------------------------------------------------
return [
// 应用地址
'app_host' => env('app.host', ''),
// 应用的命名空间
'app_namespace' => '',
// 是否启用路由
'with_route' => true,
// 默认应用
'default_app' => 'index',
// 默认时区
'default_timezone' => 'Asia/Shanghai',
// 应用映射(自动多应用模式有效)
'app_map' => [],
// 域名绑定(自动多应用模式有效)
'domain_bind' => [],
// 禁止URL访问的应用列表(自动多应用模式有效)
'deny_app_list' => [],
// 异常页面的模板文件
'exception_tmpl' => env('APP_DEBUG') ? app()->getThinkPath() . 'tpl/think_exception.tpl' : app()->getAppPath() . 'exception/laytp.tpl',
// 错误显示信息,非调试模式有效
'error_message' => '页面错误!请稍后再试~',
// 显示错误信息
'show_error_msg' => false,
];
+32
View File
@@ -0,0 +1,32 @@
<?php
// +----------------------------------------------------------------------
// | 缓存设置
// +----------------------------------------------------------------------
return [
// 默认缓存驱动
'default' => env('cache.driver', 'file'),
// 缓存连接方式配置
'stores' => [
'file' => [
// 驱动方式
'type' => 'File',
// 缓存保存目录
'path' => '',
// 缓存前缀
'prefix' => '',
// 缓存有效期 0表示永久缓存
'expire' => 0,
// 缓存标签前缀
'tag_prefix' => 'tag:',
// 序列化机制 例如 ['serialize', 'unserialize']
'serialize' => [],
],
// 'redis' => [
// 'type' => 'redis',
// ]
// 更多的缓存连接
],
];
+39
View File
@@ -0,0 +1,39 @@
<?php
// +----------------------------------------------------------------------
// | Captcha配置文件
// +----------------------------------------------------------------------
return [
//验证码位数
'length' => 5,
// 验证码字符集合
'codeSet' => '2345678abcdefhijkmnpqrstuvwxyzABCDEFGHJKLMNPQRTUVWXY',
// 验证码过期时间
'expire' => 1800,
// 是否使用中文验证码
'useZh' => false,
// 是否使用算术验证码
'math' => false,
// 是否使用背景图
'useImgBg' => false,
//验证码字符大小
'fontSize' => 25,
// 是否使用混淆曲线
'useCurve' => true,
//是否添加杂点
'useNoise' => true,
// 验证码字体 不设置则随机
'fontttf' => '',
//背景颜色
'bg' => [243, 251, 254],
// 验证码图片高度
'imageH' => 0,
// 验证码图片宽度
'imageW' => 0,
// 添加额外的验证码设置
'login' => [
'length' => 4,
'math' => true,
],
];
+9
View File
@@ -0,0 +1,9 @@
<?php
// +----------------------------------------------------------------------
// | 控制台配置
// +----------------------------------------------------------------------
return [
// 指令定义
'commands' => [
],
];
+20
View File
@@ -0,0 +1,20 @@
<?php
// +----------------------------------------------------------------------
// | Cookie设置
// +----------------------------------------------------------------------
return [
// cookie 保存时间
'expire' => 0,
// cookie 保存路径
'path' => '/',
// cookie 有效域名
'domain' => '',
// cookie 启用安全传输
'secure' => false,
// httponly设置
'httponly' => false,
// 是否使用 setcookie
'setcookie' => false,
// samesite 设置,支持 'strict' 'lax'
'samesite' => '',
];
+60
View File
@@ -0,0 +1,60 @@
<?php
return [
// 默认使用的数据库连接配置
'default' => env('database.driver', 'mysql'),
// 自定义时间查询规则
'time_query_rule' => [],
// 自动写入时间戳字段
// true为自动识别类型 false关闭
// 字符串则明确指定时间字段类型 支持 int timestamp datetime date
'auto_timestamp' => true,
// 时间字段取出后的默认时间格式
'datetime_format' => 'Y-m-d H:i:s',
// 数据库连接配置信息
'connections' => [
'mysql' => [
// 数据库类型
'type' => env('database.type', 'mysql'),
// 服务器地址
'hostname' => env('database.hostname', '127.0.0.1'),
// 数据库名
'database' => env('database.database', ''),
// 用户名
'username' => env('database.username', 'root'),
// 密码
'password' => env('database.password', ''),
// 端口
'hostport' => env('database.hostport', '3306'),
// 数据库连接参数
'params' => [],
// 数据库编码默认采用utf8
'charset' => env('database.charset', 'utf8'),
// 数据库表前缀
'prefix' => env('database.prefix', 'lt_'),
// 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
'deploy' => 0,
// 数据库读写是否分离 主从式有效
'rw_separate' => false,
// 读写分离后 主服务器数量
'master_num' => 1,
// 指定从服务器序号
'slave_no' => '',
// 是否严格检查字段是否存在
'fields_strict' => true,
// 是否需要断线重连
'break_reconnect' => false,
// 监听SQL
'trigger_sql' => env('app_debug', true),
// 开启字段缓存
'fields_cache' => false,
],
// 更多的数据库配置信息
],
];
+24
View File
@@ -0,0 +1,24 @@
<?php
return [
// 默认磁盘
'default' => env('filesystem.driver', 'public'),
// 磁盘列表
'disks' => [
'local' => [
'type' => 'local',
'root' => app()->getRuntimePath() . 'storage',
],
'public' => [
// 磁盘类型
'type' => 'local',
// 磁盘路径
'root' => app()->getRootPath() . 'public/static/storage',
// 磁盘路径对应的外部URL路径
'url' => '/static/storage',
// 可见性
'visibility' => 'public',
],
// 更多的磁盘配置信息
],
];
+27
View File
@@ -0,0 +1,27 @@
<?php
// +----------------------------------------------------------------------
// | 多语言设置
// +----------------------------------------------------------------------
return [
// 默认语言
'default_lang' => env('lang.default_lang', 'zh-cn'),
// 允许的语言列表
'allow_lang_list' => [],
// 多语言自动侦测变量名
'detect_var' => 'lang',
// 是否使用Cookie记录
'use_cookie' => true,
// 多语言cookie变量
'cookie_var' => 'think_lang',
// 多语言header变量
'header_var' => 'think-lang',
// 扩展语言包
'extend_list' => [],
// Accept-Language转义为对应语言包名称
'accept_language' => [
'zh-hans-cn' => 'zh-cn',
],
// 是否支持语言分组
'allow_group' => false,
];
Executable
+45
View File
@@ -0,0 +1,45 @@
<?php
// +----------------------------------------------------------------------
// | 日志设置
// +----------------------------------------------------------------------
return [
// 默认日志记录通道
'default' => env('log.channel', 'file'),
// 日志记录级别
'level' => [],
// 日志类型记录的通道 ['error'=>'email',...]
'type_channel' => [],
// 关闭全局日志写入
'close' => false,
// 全局日志处理 支持闭包
'processor' => null,
// 日志通道列表
'channels' => [
'file' => [
// 日志记录方式
'type' => 'File',
// 日志保存目录
'path' => app()->getRuntimePath() . '/log',
// 单文件日志写入
'single' => false,
// 独立日志级别
'apart_level' => [],
// 最大日志文件数量
'max_files' => 0,
// 使用JSON格式记录
'json' => false,
// 日志处理
'processor' => null,
// 关闭通道日志写入
'close' => false,
// 日志输出格式化
'format' => '[%s][%s] %s',
// 是否实时写入
'realtime_write' => false,
],
// 其它日志通道配置
],
];
+9
View File
@@ -0,0 +1,9 @@
<?php
// +----------------------------------------------------------------------
// | 分页设置
// +----------------------------------------------------------------------
return [
// 每页条数
'limit' => 10
];
+13
View File
@@ -0,0 +1,13 @@
<?php
return array (
'apiUrl' => 'https://api.laytp.com',
'installed' =>
array (
0 => 'apidoc',
1 => 'meditor',
),
'installedEditor' =>
array (
0 => 'meditor',
),
);

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