官方手册 https://doc.thinkphp.cn/v8_0/setup.html
安装配置
安装
composer create-project topthink/think tp
更新
composer update topthink/framework
运行
php think run
php think run -p 80
配置小皮面板
注意事项
- 配置文件路径到
public
- php版本要注意当前的框架支持的版本
- 同步host有的时候会失败 比如启动了360 就会失败 需要手动打开对文件修改
- 配置伪静态 url重写 https://doc.thinkphp.cn/v8_0/url_access.html
[ Apache ]
httpd.conf配置文件中加载了mod_rewrite.so模块
AllowOverride None 将None改为 All
把下面的内容保存为.htaccess文件放到应用入口文件的同级目录下
<IfModule mod_rewrite.c>
Options +FollowSymlinks -Multiviews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L]
</IfModule>
host文件目录 C:\Windows\System32\drivers\etc
设置域名 http://www.tpbase.cnn/
通过 http://www.tpbase.cnn/index.php/think
来访问路由
验证码扩展
composer require topthink/think-captcha
修改配置文件
config/captcha.php
//验证码字符大小
'fontSize' => 15,
// 验证码图片高度
'imageH' => 40,
// 验证码图片宽度
'imageW' => 150,
需要开启session
app/controller/middleware.php
文件
<?php
// 全局中间件定义文件
return [
// Session初始化
\think\middleware\SessionInit::class
];
配置 .env 文件
将.example.env
文件复制一份 把前缀删除 留下 .env
各种扩展
# 多应用扩展
composer require topthink/think-multi-app
# 视图扩展 安装
composer require topthink/think-view
# think助手工具库
composer require topthink/think-helper
# 验证码扩展
composer require topthink/think-captcha
# jwt
composer require firebase/php-jwt
# 数据生成扩展
composer require fakerphp/faker
# 数据迁移扩展
composer require topthink/think-migration
# http请求扩展 文档
# https://requests.ryanmccue.info/download/
composer require rmccue/requests
# 上传文件扩展 上传到远程或者第三方平台
composer require topthink/think-filesystem
开启多应用
- 删除原始的
app/controller
目录 - 在项目跟目录下 使用命令
php think build admin
来创建应用 - 将全局的
config
和route
复制一份到创建的应用里面- 开器多应用后全局的
route
会失效, - 应用里面的
config
参数 可以覆盖全选的config
参数 - 可以针对不同的应用设置不同的配置参数和相同的配置
- 开器多应用后全局的
开启 调试模式
根目录 env文件 APP_DEBUG = true
config/app.php
配置 'show_error_msg' => true
开启强制路由
config/route.php
配置
'url_route_must' => true,
thinkphp 内容相关
think 命令
php think make:conroller Upload -p
#参数 -r 资源路由 -m 关联模型 - f 强制覆盖 --api
关联模型
使用模型的时候才能用 代替的功能 连表查询
- 一对一关联
- 一对多关联
各种小功能实现
登录
视图文件路径view/login/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登录页面</title>
</head>
<body>
<form action="{:url('login')}" method="post">
用户名: <input type="text" name="username"> <br>
密码: <input type="password" name="password"> <br>
<img src="{:captcha_src()}" onclick="this.src='{:captcha_src()}?_='+ Date.now()" alt=""><br>
验证码<input type="text" name="captcha"> <br>
<input type="submit" value="提交">
</form>
</body>
</html>
路由文件路径route/app.php
use think\facade\Route;
Route::rule('login','index/login','get|post');
控制器文件路径app/controller/Index.php
public function login()
{
// return '12';
if ($this->request->isGet()) {
// 如果是get请求,返回登录页面
return view('login/index');
} else if ($this->request->isPost()) {
// 如果是post请求,进行登录处理
// 获取表单提交的数据
$data = $this->request->post();
// 对数据进行校验
$this->validate($data, [
//校验规则
'username' => 'require',
'password' => 'require',
'captcha|验证码'=>'require|captcha'
], [
//校验校验失败返回
'username.require' => '用户名不能为空',
'password.require' => '密码不能为空',
'captcha.require'=>'验证码不能为空',
]);
//通过校验
//对账号和密码进行验证
//查找数据库中是否有改账号 并且密码是否正确
// 设置一个密码的盐
$salt = 'aidfhy48y289y523;``o2b';
// 对处理密码
$password = md5(md5($data['password']) . $salt);
$where=[
'username'=> $data['username'],
'password'=> $password
];
//到数据库中进行查询判断是否能查到数据
// $res = model('')->login($where);
//如果查到数据 登录成功 否则失败
$res= rand(1,10) %2 ? true : false ;
// $res= rand(1,10) %2 ?? false ;
//处理信息 对 信息进行入库处理 获取跳转到对应的页面
if($res){
return "<script>alert('登录成功')</script>";
// return json(['code'=> 1,'msg'=> '登录成功']);
}else{
return "<script>alert('登录成功')</script>";
// return json(['code'=> 10001,'msg'=> '登录失败']);
}
}
}
总结
- 验证码功能,要 手动安装 并配置开启session ,视图功能也要安装
- 验证码获取当然还有其他获取方式看手册
<img src="{:captcha_src()}" onclick="this.src='{:captcha_src()}?_='+ Date.now()" alt="">
- 点击的时候设置对自身操作设置一个随机的参数放置浏览器因为缓存功能不进行请求
- 用到了路由,跟校验
- 一般的数据请求
- 获取请求数据
$this->response()->post()
- 对数据进行校验
$this->validate()
- 处理数据 入库处理或者其他
- 返回数据 json数据或者 视图模板
- 获取请求数据
- 要注意请求的数据name名称 跟校验的 名称是否一致
- tp框架初始化默认的是路由模式是混合模式
- 可以通过路由注册后的路由地址进行访问
- 也可以不注册路由地址直接访问
- 可以设置为强制模式 只能通过自定义的规则进行访问
退出登录
路由 route/app.php
route::rule('logout','index/logout');
控制器 app/controller/index.php
public function logout()
{
// 清空token 将需清空的token存入缓存,再次使用时,会读取缓存进行判断
// 从请求头中获取token
$token = $this->request->header('token');
if(empty($token)){
//获取失败 返回失败信息
}
// 获取主动失效token的数组
$delete_token = cache('delete_token')??[];
//将用户token添加到数组中
$delete_token[] = $token;
cache('delete_token', $delete_token, 86400);
// 返回成功标识 或者 跳转到首页
}
总结
使用了缓存功能 设置3600秒后过期
Cache::set('name', $value, 3600);
登录检测
注册中间件
除了登录相关接口,其他接口都需要登录后才能访问。
设置一个全局的中间件来进行登录检测
快速生成一个中间件
php think make:middleware Check
这个指令会 app/middleware目录下面生成一个Check中间件。 简单的代码如下
class Check
{
//无需进行登录检测的请求 控制器/操作
public static $no_login = ['login/login', 'login/captcha'];
/**
* 处理请求
*
* @param \think\Request $request
* @param \Closure $next
* @return Response
*/
public function handle($request, \Closure $next)
{
try {
// dd($request);
$path = $request->controller(true) . '/' . $request->action(true);
if (!in_array($path, self::$no_login)) {
//不在白名单里面
//校验登录信息
//进行token校验获取用户信息
$user_id = rand(0,10)%2 ? 1 : 4; //管理员或者普通用户 测试用
// 失败返回失败相应
//权限验证
/**
* 1.获取用户角色 信息
* 是超级管理员 直接通过
* 不是超级管理员 进行权限验证
* 2. 不是超级管理员
* 查询当前用户的角色权限信息
* 查询当前 控制器/方法
* 判断当前的权限id是否在 用户 role_auth_ids 的范围中
*
*/
$user = Db::name('admin')->where('id', $user_id)->find();
$role_id = $user['role_id']; //用户角色id
if ($role_id != 1) {
//不是超级管理员 需要判断权限
$role_auth_ids = Db::name('role')->where('id', $role_id)->value('role_auth_ids');
$role_auth_ids = explode('_', $role_auth_ids);
//根据当前控制器 方法查询条件
$auth = Db::name('auth')->where('auth_c', $request->controller(true))->where('auth_a', $request->action(true))->field('id')->find();
if (empty($auth) or in_array($auth['id'], $role_auth_ids) == false) {
//如果权限不存在 返回错误信息
return json(['code' => 403, 'msg' => '无权限访问']);
}
}
//失败返回失败相应
//成功后将用户信息设置到请求头中
// 用户信息
$userId = ['user_id' => $user_id];
$request->withPost(array_merge($request->post(), $userId));
$request->withGet(array_merge($request->get(), $userId));
}
return $next($request);
} catch (\Exception $th) {
//返回异常
$err = [
'code' => 0,
'msg' => $th->getMessage()
];
return json($err);
}
}
}
注册全局中间件注册方法 在 app/middleware.php
文件下注册
// 全局中间无法获取当前的控制器和方法
\app\middleware\Check::class,
注册为路由中间件 route/app.php
//添加一个路由分组把所有的请求都包起来 添加中间件注册
Route::group(function(){
//...
Route::rule('login','index/login','get|post');
Route::resource('auth', 'auth');
})->middleware(\app\middleware\Check::class);
或者在路由配置文件里面做路由全局注册 config/route/php
'middleware' => [
app\middleware\Check::class,
],
注意: 如果使用 Route::get('auth/nav', AuthC::class.'/nav');
这种全路径控制器+方法的情况下面获取不到控制器跟方法名
可以通过 $request->rule()->getName(); 来获取全路径的控制器方法
总结
- 使用了路由全局中间件来做 为每一个请求都进行校验处理
- 分三重校验 白名单页面 , 使用jwt对token 进行校验, 最后 权限校验
- 白名单页面直接通过
- 校验token 来判断用户登录状态
- 权限校验 来判断 对应的接口是否通过
用户注册
短信验证注册 去第三方平台注册账号 购买短信服务
一般都有接口文档 看着文档照着做就行
有的需要安装扩展 直接调用就行
会有sdk 或者 demo 有的需要自己封装 一般需要用到 http请求扩展
// 手机号注册
// 获取请求参数
// 参数检测 手机号 是否合法 用正则 再判断 在数据库中是否唯一 unique:model,phone
// 生成一个随机的验证码数据 rand(1000,9999) 再把他组合到验证信息里面
// 根据第三方接口去发送请求 如果返回了成功 就把验证码和手机号存入缓存中
// 设置手机号为key 验证码为value 设置过期时间 一般5分钟 5*60 添加一个当前的时间戳
// 还可以设置 1 分钟内不允许重复发送 可以用ip地址+手机号 来进行校验
// 前端设置定时器来做
// 验证校验成功后 要把缓存中的验证吗给清除了
限制ip(同一个ip一天只能发送多少次) 框架中请求对象request的ip方法获取ip地址
限制手机号(同一个手机号一天只能发送多少次)
jwt认证 框架无关
JWT 是一种无状态的身份验证机制,所有必要的信息都编码在 token 中 数据进行加密放在token中
session 是一种有状态的机制,需要在服务器端保存用户会话状态。
封装JWT工具类 (参考https://github.com/lcobucci/jwt/tree/5.3.x
)
安装 composer require lcobucci/jwt
https://github.com/firebase/php-jwt?tab=readme-ov-file
另一款扩展 composer require firebase/php-jwt
如果要这种形式使用认证 需要设置一下.htaccess文件
Authorization: Bearer
修改public/.htaccess文件,通过apache重写,处理HTTP请求中的Authorization字段
(不处理,php中接收不到HTTP_AUTHORAZATION字段信息)
RewriteCond %{HTTP:Authorization} ^(.+)$
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
代码
<?php
namespace app\server;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
class Token
{
//从配置信息这种或取唯一字符串,你可以随便写比如md5('token')
private static $key = 'your_secret_key';
/**
* 生成token
* @param $uid
* @return mixed
*/
public static function getToken($uid)
{
//获取当前时间戳
$currentTime = time();
$data = array(
"iss" => self::$key, //签发者 可以为空
"aud" => '', //面象的用户,可以为空
"iat" => $currentTime, //签发时间
"nbf" => $currentTime, //立马生效
"exp" => $currentTime + 7200, //token 过期时间 两小时
"data" => [ //记录的userid的信息,这里是自已添加上去的,如果有其它信息,可以再添加数组的键值对
'uid' => $uid,
]
);
//生成token
$token = JWT::encode($data, self::$key, "HS256"); //根据参数生成了 token
return $token;
}
/**
* 校验token时效性
*/
public static function chekToken($token)
{
$status = array("code" => 2);
try {
JWT::$leeway = 60; //当前时间减去60,把时间留点余地
$decoded = JWT::decode($token, new Key(self::$key, 'HS256')); //HS256方式,这里要和签发的时候对应
$arr = (array)$decoded;
$res['code'] = 1;
$res['data'] = $arr['data'];
return $res;
} catch (\Firebase\JWT\SignatureInvalidException $e) { //签名不正确
$status['msg'] = "签名不正确";
return $status;
} catch (\Firebase\JWT\BeforeValidException $e) { // 签名在某个时间点之后才能用
$status['msg'] = "token失效";
return $status;
} catch (\Firebase\JWT\ExpiredException $e) { // token过期
$status['msg'] = "token失效";
return $status;
} catch (\Exception $e) { //其他错误
$status['msg'] = "未知错误";
return $status;
}
}
}
总结
- 使用了 firebase/php-jwt 来封装token的生成和校验
- 使用 静态调用的方法
分页样式
一个简单的分页样式
<style>
.pagination li {
display: inline-block;
margin-right: -1px;
padding: 5px;
border: 1px solid #e2e2e2;
min-width: 20px;
text-align: center;
}
.pagination li.active {
background: #009688;
color: #fff;
border: 1px solid #009688;
}
.pagination li a {
display: block;
text-align: center;
}
</style>
RBAC权限管理
- 用户 - 角色 - 权限
- 用户表 admin 字段 role_id 对应 角色表 id 也可以设置为role_id 对应 角色表多个id 逻辑和角色表相同
- 角色表 role 字段 auth_ids 对应 权限表 id 字段的多个值
- 权限表 auth
数据生成工具
官网https://fakerphp.github.io/
地址
https://packagist.org/packages/fakerphp/faker
composer require fakerphp/faker
数据库迁移工具
安装
composer require topthink/think-migration
相关think 命令
#创建数据迁移文件
php think migrate:create MyRbac
#运行
php think migrate:run
#回滚
php think migrate:rollback
#创建数据填充文件
php think seed:create RbacData
#执行数据填充文件
php think seed:run
配置数mysql数据库 在env文件中
DB_TYPE = mysql
DB_HOST = 127.0.0.1
DB_NAME = rbac
DB_USER = root
DB_PASS = root
DB_PORT = 3306
DB_CHARSET = utf8
表设计
默认自增主键为 id字段
用户表 admin
表名 | 类型 | 注释 |
---|---|---|
username | varchar(15) | 用户名 |
password | char(32) | 密码使用md5加密 |
role_id | tyinyint(3) | 角色id 1-999 |
角色表 role
表名 | 类型 | 注释 |
---|---|---|
role_name | varchar(20) | 角色名 |
desc | varchar(255) | 描述 |
role_auth_ids | varchar(255) | 角色权限关联 集合 |
权限表表 auth
表名 | 类型 | 注释 |
---|---|---|
auth_name | varchar(20) | 权限名称 |
pid | smallint(6) | 父id |
pid_path | varchar(255) | 家族图谱 |
level | tinyint(1) | 级别:0,1,2 |
auth_c | varchar(32) | 控制器 |
auth_a | varchar(32) | 操作方法 |
is_nav | tinyint(1) | '是否作为菜单显示 1是 0否' |
sort | smallint(6) | 排序 |
数据准备
- 创建数据迁移文件 文件路径
database\migrations
php think migrate:create MyRbac
- 创建数据填充文件 文件路径
database\seeds
php think seed:create RbacData
注意:
执行数据迁移的时候 存在change()的时候 up()和down()方法不会执行
执行数据填充的时候 报错must be compatible with Phinx\Seed\AbstractSeed::run(): void
需要手动添加声明类型 在run()
方法前面加上public function run(): void
以明确表示该方法没有返回值。
数据迁移代码
<?php
use think\migration\Migrator;
class MyRbac extends Migrator
{
public function change()
{
//用户表 admin 默认会添加id
$this->table('admin')
->addColumn('username', 'string',array('limit' => 15,'default'=>'','comment'=>'用户名,登陆使用'))
->addColumn('password', 'string',array('limit' => 32,'default'=>md5('123456'),'comment'=>'用户密码'))
->addColumn('role_id', 'integer',array('limit' => 3,'default'=>1,'comment'=>'用户角色'))
->create();
//角色表 role
$this->table('role')
->addColumn('role_name', 'string',array('limit' => 15,'default'=>'','comment'=>'角色名'))
->addColumn('desc', 'string',array('limit' => 255,'comment'=>'描述'))
->addColumn('role_auth_ids', 'string',array( 'limit' => 255,'comment'=>'角色权限关联'))
->create();
//权限表 auth
$this->table('auth')
->addColumn('auth_name', 'string',array('limit' => 20,'default'=>'','comment'=>'权限名称'))
->addColumn('pid', 'integer',array('limit' => 6,'comment'=>'父id'))
->addColumn('pid_path', 'string',array('limit' => 255,'comment'=>'家族图谱'))
->addColumn('level', 'integer',array('limit' => 1,'comment'=>'级别:0,1,2'))
->addColumn('auth_c', 'string',array('limit' => 32,'comment'=>'控制器'))
->addColumn('auth_a', 'string',array('limit' => 32,'comment'=>'操作方法'))
->addColumn('is_nav', 'integer',array('limit' => 1,'comment'=>'是否作为菜单显示 1是 0否'))
->addColumn('sort', 'integer',array('limit' => 6,'comment'=>'排序'))
->create();
}
}
运行数据迁移php think migrate:run
数据填充代码
<?php
use think\migration\Seeder;
class RbacData extends Seeder
{
public function run(): void
{
//创建faker对象
$faker = Faker\Factory::create('zh_CN'); //选择中文
//生成随机用户数据
$table = $this->table('admin');
$data = [];
for ($i = 0; $i < 5; $i++) {
# code...
$data[] = [
'username' => 'admin'.$i,
//随机1-5
'role_id' => rand(1, 5)
];
}
$table->insert($data)->saveData();
$data = [];
//生成随机角色数据
$table = $this->table('role');
//生成随机数据
$rouleName = ['超级管理员', '管理员', '客服', '编辑', '用户'];
for ($i = 0; $i < 4; $i++) {
$data[] = [
'role_name' => $rouleName[$i],
'desc' => $faker->text(20),
'role_auth_ids' => '1_2_3_4_5_6_7',
];
}
$table->insert($data)->saveData();
//生成随机权限表数据
// $data = [];
// $table = $this->table('auth');
//生成随机数据
// for ($i = 0; $i < 10; $i++) {
// $data[] = [
// 'auth_name' => $faker->name,
// 'pid' => 0,
// 'pid_path' => '',
// 'level' => rand(1, 3),
// 'auth_c' => '',
// 'auth_a' => '',
// 'is_nav' => rand(0, 1),
// 'sort' => rand(1, 10)
// ];
// }
// $table->insert($data)->saveData();
}
}
运行数据填充php think migrate:run
权限数据用这个来模拟把上面生成的数据可以删除了
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (1, '首页', 0, '0', 0, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (2, '平台', 0, '0', 0, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (3, '商城', 0, '0', 0, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (4, '运营', 0, '0', 0, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (5, '系统配置', 2, '0_2', 1, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (6, '权限管理', 2, '0_2', 1, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (7, '配置管理', 5, '0_2_5', 2, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (8, '网站设置', 5, '0_2_5', 2, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (9, '附件管理', 5, '0_2_5', 2, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (10, '菜单管理', 5, '0_2_5', 2, 'auth', 'index', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (11, '管理员管理', 6, '0_2_6', 2, 'admin', 'index', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (12, '角色管理', 6, '0_2_6', 2, 'role', 'index', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (13, '管理日志', 6, '0_2_6', 2, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (14, '订单管理', 3, '0_3', 1, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (15, '商品管理', 3, '0_3', 1, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (16, '会员管理', 3, '0_3', 1, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (17, '基础设置', 3, '0_3', 1, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (18, '众筹管理', 3, '0_3', 1, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (19, '订单管理', 14, '0_3_14', 2, 'order', 'index', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (20, '投诉订单', 14, '0_3_14', 2, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (21, '退款订单', 14, '0_3_14', 2, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (22, '商品分类', 15, '0_3_15', 2, 'category', 'index', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (23, '商品品牌', 15, '0_3_15', 2, 'brand', 'index', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (24, '商品模型', 15, '0_3_15', 2, 'type', 'index', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (25, '商品列表', 15, '0_3_15', 2, 'goods', 'index', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (26, '回收站', 15, '0_3_15', 2, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (27, '商品属性', 15, '0_3_15', 2, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (28, '商品规格', 15, '0_3_15', 2, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (29, '评价管理', 15, '0_3_15', 2, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (30, '举报管理', 15, '0_3_15', 2, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (31, '商品咨询', 15, '0_3_15', 2, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (32, '会员等级', 16, '0_3_16', 2, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (33, '会员管理', 16, '0_3_16', 2, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (34, '广告管理', 17, '0_3_17', 2, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (35, '银行管理', 17, '0_3_17', 2, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (36, '支付管理', 17, '0_3_17', 2, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (37, '众筹管理', 18, '0_3_18', 2, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (38, '促销管理', 4, '0_4', 1, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (39, '推荐管理', 4, '0_4', 1, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (40, '统计报表', 4, '0_4', 1, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (41, '财务管理', 4, '0_4', 1, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (42, '店铺推荐', 39, '0_4_39', 2, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (43, '商品推荐', 39, '0_4_39', 2, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (44, '品牌推荐', 39, '0_4_39', 2, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (45, '商品销售排行', 40, '0_4_40', 2, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (46, '店铺销售排行', 40, '0_4_40', 2, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (47, '销售额统计', 40, '0_4_40', 2, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (48, '销售订单统计', 40, '0_4_40', 2, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (49, '新增会员统计', 40, '0_4_40', 2, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (50, '资金管理', 41, '0_4_41', 2, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (51, '提现申请', 41, '0_4_41', 2, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (52, '结算申请', 41, '0_4_41', 2, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (53, '商家结算', 41, '0_4_41', 2, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (54, '删除日志', 13, '0_2_6_13', 3, '', '', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (55, '添加角色', 12, '0_2_6_12', 3, 'role', 'create', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (56, '添加角色-保存', 12, '0_2_6_12', 3, 'role', 'save', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (57, '修改角色', 12, '0_2_6_12', 3, 'role', 'edit', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (58, '修改角色-保存', 12, '0_2_6_12', 3, 'role', 'update', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (59, '删除角色', 12, '0_2_6_12', 3, 'role', 'delete', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (60, '添加管理员', 11, '0_2_6_11', 3, 'admin', 'create', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (61, '添加管理员-保存', 11, '0_2_6_11', 3, 'admin', 'save', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (62, '修改管理员', 11, '0_2_6_11', 3, 'admin', 'edit', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (63, '修改管理员-保存', 11, '0_2_6_11', 3, 'admin', 'update', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (64, '删除管理员', 11, '0_2_6_11', 3, 'admin', 'delete', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (65, '添加权限', 10, '0_2_5_10', 3, 'auth', 'create', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (66, '添加权限-保存', 10, '0_2_5_10', 3, 'auth', 'save', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (67, '修改权限', 10, '0_2_5_10', 3, 'auth', 'edit', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (68, '修改权限-保存', 10, '0_2_5_10', 3, 'auth', 'update', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (69, '删除权限', 10, '0_2_5_10', 3, 'auth', 'delete', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (70, '添加商品分类', 22, '0_3_15_22', 3, 'category', 'create', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (71, '添加商品分类-保存', 22, '0_3_15_22', 3, 'category', 'save', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (72, '修改商品分类', 22, '0_3_15_22', 3, 'category', 'edit', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (73, '修改商品分类-保存', 22, '0_3_15_22', 3, 'category', 'update', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (74, '删除商品分类', 22, '0_3_15_22', 3, 'category', 'delete', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (75, '添加商品品牌', 23, '0_3_15_23', 3, 'brand', 'create', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (76, '添加商品品牌-保存', 23, '0_3_15_23', 3, 'brand', 'save', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (77, '修改商品品牌', 23, '0_3_15_23', 3, 'brand', 'edit', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (78, '修改商品品牌-保存', 23, '0_3_15_23', 3, 'brand', 'update', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (79, '删除商品品牌', 23, '0_3_15_23', 3, 'brand', 'delete', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (80, '添加商品模型', 24, '0_3_15_24', 3, 'type', 'create', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (81, '添加商品模型-保存', 24, '0_3_15_24', 3, 'type', 'save', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (82, '修改商品模型', 24, '0_3_15_24', 3, 'type', 'edit', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (83, '修改商品模型-保存', 24, '0_3_15_24', 3, 'type', 'update', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (84, '删除商品模型', 24, '0_3_15_24', 3, 'type', 'delete', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (85, '添加商品', 25, '0_3_15_25', 3, 'goods', 'create', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (86, '添加商品-保存', 25, '0_3_15_25', 3, 'goods', 'save', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (87, '修改商品', 25, '0_3_15_25', 3, 'goods', 'edit', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (88, '修改商品-保存', 25, '0_3_15_25', 3, 'goods', 'update', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (89, '删除商品', 25, '0_3_15_25', 3, 'goods', 'delete', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (90, '首页', 1, '0_1', 1, '', '', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (91, '首页', 90, '0_1_90', 2, 'index', 'index', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (92, '接口', 0, '0', 0, '', '', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (93, '图片验证码', 92, '0_92', 1, 'login', 'captcha', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (94, '登录', 92, '0_92', 1, 'login', 'login', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (95, '退出', 92, '0_92', 1, 'login', 'logout', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (96, '获取菜单权限', 92, '0_92', 1, 'auth', 'nav', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (97, 'logo图片上传', 92, '0_92', 1, '', '', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (98, '多图片上传', 92, '0_92', 1, '', '', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (99, '相册图片删除', 25, '0_3_15_25', 3, 'goods', 'delpics', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (100, '订单修改', 19, '0_3_14_19', 3, 'order', 'edit', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (101, '订单修改-保存', 19, '0_3_14_19', 3, 'order', 'update', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (102, '订单删除', 19, '0_3_14_19', 3, 'order', 'delete', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (103, '订单详情', 19, '0_3_14_19', 3, 'order', 'read', 0, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (104, '首页啊', 90, '0_1_90', 2, 's', 'y', 1, 50);
INSERT INTO `auth` (`id`, `auth_name`, `pid`, `pid_path`, `level`, `auth_c`, `auth_a`, `is_nav`, `sort`) VALUES (105, 'sss', 104, '0_1_90_104', 3, '1', '3', 1, 50);
控制器逻辑
创建对应的控制器
php think make:controller user
php think make:controller role
php think make:controller auth
注册资源路由 在route/app.php
文件中
Route::resource('user', 'user');
Route::resource('role', 'role');
Route::resource('auth', 'auth');
user(用户控制器)
app\controller\user.php
<?php
declare(strict_types=1);
namespace app\controller;
use app\BaseController;
use think\facade\Db;
use think\Request;
class user extends BaseController
{
//获取用户列表
public function index()
{
//获取用户列表 keyword 搜索关键词 page 分页数据 返回分页数据
$param = $this->request->param();
$where=[];
//搜索条件
if(!empty($param['keyword'])){
$keyword = $param['keyword'];
$where['u.username']=['like',"%$keyword%"];
}
$list = Db::name('admin')
->alias('u')
->leftJoin('role r', 'u.role_id=r.id')
->field('u.id,u.username,u.role_id,r.role_name')
->where($where)
->paginate(10);
if (empty($list)) {
$res = [
'code' => 1001,
'msg' => '暂无数据',
];
return json($res);
}
$res = [
'code' => 200,
'msg' => $list,
];
return json($res);
}
//修改用户数据
public function update(Request $request, $id)
{
//获取请求信息
$param = $request->param(['username', 'password', 'role_id']);
// 校验数据
try {
$this->validate(
$param,
['username' => 'require', 'password' => 'require', 'role_id' => 'require'],
['username.require' => '用户名不能为空', 'password.require' => '密码不能为空', 'role_id.require' => '角色不能为空']
);
} catch (\Exception $th) {
return json(['code' => 1001, 'msg' => $th->getMessage()]);
}
// 校验role_Id是否合法
$role = Db::name('role')->where('id', $param['role_id'])->find();
if (!$role) {
return json(['code' => 1002, 'msg' => '角色不存在']);
}
//修改用户信息 为0没有修改任何数据 为1 修改成功
$res = Db::name('admin')->where('id', $id)->update($param);
if ($res) {
return json(['code' => 1, 'msg' => '修改成功']);
}
return json(['code' => 0, 'msg' => '修改失败']);
}
// 删除用户
public function delete($id)
{
//直接用软删除
// 不能删除超级管理员 不能删除自己
$user = Db::name('user')->where('id', $id)->useSoftDelete('delete_time', time())->delete();
return json(['code' => 1, 'msg' => '删除成功']);
}
}
role(角色控制器)
跟上逻辑类似
<?php
declare(strict_types=1);
namespace app\controller;
use think\facade\Db;
use think\Request;
class role
{
// 获取角色列表
public function index()
{
//从数据库中查询数据
$result = Db::table('role')->select();
//返回数据
return json($result);
}
//新增角色信息
public function save(Request $request)
{
//获取请求数据 post
//校验请求数据 this->validate()
//将数据存入数据库 如果是单个权限id要进行拼装 implode('_',$data['role_ids'])
//返回信息
}
// 编辑角色信息
public function update(Request $request, $id)
{
//获取请求数据 params
//校验请求数据 this->validate()
//将数据存入数据库
//返回信息
}
//删除指定资源
public function delete($id)
{
//删除逻辑
//超级管理员角色不能删除
//角色下有关联人员 不能删
//直接删除使用软删除
}
}
auth(权限控制器)
公共方法(转为树状-无极限)
在公共文件里面写入两个函数处理权限列表
app\common.php
<?php
// 应用公共文件
if (!function_exists('get_cate_list')) {
//递归函数 实现无限级分类列表
function get_cate_list($list,$pid=0,$level=0) {
static $tree = array();
foreach($list as $row) {
if($row['pid']==$pid) {
$row['level'] = $level;
$tree[] = $row;
get_cate_list($list, $row['id'], $level + 1);
}
}
return $tree;
}
}
if(!function_exists('get_tree_list')){
//引用方式实现 父子级树状结构
function get_tree_list($list){
//将每条数据中的id值作为其下标
$temp = [];
foreach($list as $v){
$v['son'] = [];
$temp[$v['id']] = $v;
}
//获取分类树
foreach($temp as $k=>$v){
$temp[$v['pid']]['son'][] = &$temp[$v['id']];
}
return isset($temp[0]['son']) ? $temp[0]['son'] : [];
}
}
/**
* arr_old 传入的数组
* key 指定自身id的名称 默认为id
* pid 指定父级id的名称 默认为pid
* son 指定子集的名称 默认为son
*/
if (!function_exists('get_tree_rec')) {
//递归函数 实现无限级分类列表
function get_tree_rec($arr_old, $key = 'id', $pid = 'pid', $son = 'son', $id = [])
{
$ido = [];
if (empty($arr_old)) {
//如果为空说明当前的id是所有的id 可以进行组和
//倒序循环
for ($i = count($id) - 1; $i > 0; $i--) {
foreach ($id[$i] as $v1) {
foreach ($id[$i - 1] as &$v2) {
if ($v2[$key] == $v1[$pid]) {
//设置子名称
$v2[$son][] = $v1;
}
}
}
}
return $id[0];
}
$res = array_filter($arr_old, function ($v) use (&$ido, &$id, $pid, $key) {
// echo ($v[$pid] ."\n");
if ($v[$pid] == 0) {
array_push($ido, $v);
return false;
} else {
//如果数组中存在该数据
if (!empty($id) and count($id) > 0) {
// print_r(array_column(array_slice($id, -1)[0], $key));
$arr = array_column(array_slice($id, -1)[0], $key);
if (in_array($v[$pid], $arr)) {
array_push($ido, $v);
return false;
}
}
}
return true;
});
array_push($id, $ido);
return get_tree_rec($res, $key, $pid, $son, $id);
}
}
if (!function_exists('tree')) {
//递归函数 实现无限级分类列表
// * @param array Array 总数据
// * @param pid Int 父级id
// * @return Array
function tree(array $array, int $pid = 0): array
{
$tree = array();
foreach ($array as $key => $value) {
if ($value['pid'] == $pid) {
$value['children'] = tree($array, $value['id']);
if (!$value['children']) {
unset($value['children']);
}
$tree[] = $value;
}
}
return $tree;
}
}
权限控制逻辑
<?php
declare(strict_types=1);
namespace app\controller;
use app\BaseController;
use think\facade\Db;
use think\Request;
class auth extends BaseController
{
/**
* 显示资源列表
*
* @return \think\Response
*/
public function index()
{
//获取数据
$param = $this->request->param();
//处理数据
//返回数据
$result = Db::name('auth')->select()->toArray();
if (!empty($param['type']) and $param['type'] == 'tree') {
//父子级树状列表
$list = get_tree_list($result);
} else {
// 无限级分类列表
$list = get_cate_list($result);
}
return json($list);
}
//新增权限
public function save(Request $request)
{
//接受数据
$param = $request->param();
//参数检查
try {
$validate = $this->validate($param, [
'auth_name|权限名称' => 'require',
'pid|上级权限' => 'require',
'id_nav|菜单权限' => 'require',
// 'auth_c|控制器' => 'require',
// 'auth_a|方法' => 'require',
]);
} catch (\Exception $e) {
//返回错误数据
return json(['code' => 0, 'msg' => $e->getMessage()]);
}
//处理数据(是否顶级,级别和pid_path处理)
if ($param['pid'] == 0) {
$param['level'] = 0; //级别
$param['pid_path'] = 0; //上级路径
$param['auth_c'] = 0; //控制器
$param['auth_a'] = 0; //方法
} else {
//不是顶级权限
//获取上级权限信息
$p_info = Db::name('auth')->where('id', $param['pid'])->find();
if (empty($p_info)) {
return json(['code' => 0, 'msg' => '上级权限不存在']);
}
//设置级别+1 家族图谱 拼接
$param['level'] = $p_info['level'] + 1;
$param['pid_path'] = $p_info['pid_path'] . '_' . $p_info['id'];
}
//保存数据
$result = Db::name('auth')->strict(false)->insertGetId($param);
if ($result) {
$info = Db::name('auth')->where('id', $result)->find();
return json(['code' => 1, 'msg' => '添加成功', 'data' => $info]);
}
}
//获取指定权限
public function read($id)
{
//查询指定数据
$auth = Db::name('auth')->where('id', $id)->withoutField('sort')->find();
//返回数据
}
/**
* 显示编辑资源表单页.
*
* @param int $id
* @return \think\Response
*/
public function edit($id)
{
//
}
// 查询菜单权限接口
public function nav()
{
//获取登录的用户id 如果是超级管理员 有所有的权限
//获取当前用户id
$user_id = $this->request->param('user_id');
//查询角色信息
$role_id = Db::name('admin')->where('id', $user_id)->value('role_id');
//判断是否是超级管理员
if ($role_id == 1) {
//如果是超级管理元 直接 查询所有菜单权限 is_nav= 1
$data = Db::name('auth')->where('is_nav', 1)->select();
} else {
//如果不是超级管理员 查询当前角色所拥有的权限
$role = Db::name('role')->where('id', $role_id)->find(); //查询角色信息
$role_auth_ids = explode('_', $role['role_auth_ids']); //获取当前角色所拥有的权限id
//插叙权限列表
$data = Db::name('auth')->where('is_nav',1)->where('id', 'in', $role_auth_ids)->select();
}
//转化为父子级树状结构
$data = tree($data->toArray());
//返回数据
return json(['code' => 1, 'msg' => '获取成功', 'data' => $data]);
}
//权限修改
public function update(Request $request, $id)
{
//接受数据
$param = $request->param();
//如果没有pid则为顶级权限
if (empty($param['pid'])) {
$param['pid'] = 0;
}
//判断是否为导航
if (empty($param['is_nav'])) {
$param['is_nav'] = $param['radio'];
}
//参数检测
try {
$this->validate($param, [
'auth_name|权限名称' => 'require',
'pid|上级权限' => 'require',
'id_nav|菜单权限' => 'require',
// 'auth_c|控制器' => 'require',
// 'auth_a|方法' => 'require',
]);
} catch (\Throwable $th) {
return json(['code' => 0, 'msg' => $th->getMessage()]);
}
// 修改数据(是否顶级,级别和pid_path处理
$auth = Db::name('auth')->where('id', $id)->find();
if (empty($auth)) {
return json(['code' => 0, 'msg' => '没有找到该权限']);
}
if ($param['pid'] == 0) {
//如果修改顶级权限
$param['level'] = 0;
$param['pid_path'] = 0;
} else if ($param['pid'] != $auth['pid']) {
//如果修改了上级权限 需要重新设置level级别 和pid_path图谱
$p_auth = Db::name('auth')->where('id', $param['pid'])->find(); //获取父级权限
if (empty($p_auth)) {
return json(['code' => 0, 'msg' => '没有找到该权限']);
}
$param['level'] = $p_auth['level'] + 1; // 自身的级别=父级别+1
$param['pid_path'] = $p_auth['pid_path'] . '_' . $p_auth['id']; // 自身的pid_path= 父级的pid_ath+父级的id
}
// 更新数据
$result = Db::name('auth')->update($param);
if ($result != 0) {
return json(['code' => 1, 'msg' => '修改成功']);
}
}
//删除权限
public function delete($id)
{
//判断是否有子权限 用pid字段=自身的id 看能不能找到对应的数据
//没有的话删除 有的话直接删除
}
}
权限检测
总结
- 删除注意事项
- 删除权限要判断否有子权限
- 删除角色的时候判断是否有用户在使用该角色
- 超级管理员不能删除
- 自身也不能
- 做权限管理的时候不能注册为
全局中间件
无法获取 当前请求的控制器和方法 注册为路由全局中间件
- 获取列表数据和查询数据合并在一个方法里面
文件上传
上传文件扩展
composer require topthink/think-filesystem
单文件上传 多文件上传
#创建一个普通的控制器
php think make:conroller Upload -p
#参数 -r 资源路由 -m 关联模型 - f 强制覆盖 --api
<?php
declare(strict_types=1);
namespace app\controller;
use app\BaseController;
use think\facade\Filesystem;
class Upload extends BaseController
{
//单图片上传
public function upImg()
{
//接收参数 type =1 为logo图片
if(empty($this->request->param('type'))){
return json(['code'=>0,'msg'=>'缺少参数']);
}
//获取传过来的图片文件 如果上传单文件 那么这里获取的文件为1个文件 下面的循环遍历就不用了
$files = $this->request->file('image');
// 文件名集合
$savename=[];
try {
//['image'=>'fileSize:10240|fileExt:jpg|image:200,200,jpg'] 文件校验参数 文件大小 文件后缀 图像尺寸和类型
$this->validate($files, ['image' => 'fileSize:102400|fileExt:jpg']);
//上传到本地服务器
foreach ($files as $file) {
$savename[] = Filesystem::disk('public')->putFile('topic', $file);
}
} catch (\Throwable $th) {
return json(['code' => 400, 'msg' => $th->getMessage()]);
}
//返回上传成功的图片地址 把图片地址返回到 前端页面 或者 把图片地址存入数据库
// 图片地址注意事项 前端地址需要完整的域名 可以在 返回给前端的的时候主动加上
// 列如 config('baseUrl.baseUrl').$davename[0];
// 如果需要在服务器上处理文件 文件的路径上 使用相对路由也可以 在路径上加上 ./
// 如果要删除文件 使用绝对路径 unLink() root_parh.DS.'public'.DS.存放在数据库中的相对路径
// define('DS', DIRECTORY_SEPARATOR); DS为斜杠符号
return json(['code' => 200, 'msg' => '上传成功', 'data' => ['src' => $savename]]);
}
}
第三方登录
地址:有官方文档
腾讯开放平台:https://open.tencent.com/
腾讯开放平台注册:http://open.qq.com/reg
QQ互联平台网站:https://connect.qq.com/
登录成功后会有一个 openid 用户唯一标识 给到后台
还能获取 qq昵称 qq头像 用户性别 一些基本信息
关联已注册的用户 来进行快捷登录
购物车
未登录:可以将购物车数据存储在cookie。
登录后:将购物车数据存储在数据表,并和用户关联。
在用户登录时,将cookie中的购物车数据迁移到数据表。
购物车中要存储的数据
商品id SKU的id 购买数量
25 130 20
- 未登录的时候
- 加入购物车 , 修改商品数量 , 删除商品 都在前端操作
- 数据存放到 cookie 中 或者存放到 localstorege中 进行前端持久化
- 未登录如果点击购买 需要 跳转到登录页面 登录后再次跳转到下单页面
- 登录后需要对数据进行合并
- 已经登录请求
- 直接发送请求到后端处理
购物车表
CREATE TABLE `cart` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL DEFAULT '0' COMMENT '用户id',
`goods_id` int(11) NOT NULL DEFAULT '0' COMMENT '商品id',
`number` int(11) NOT NULL DEFAULT '0' COMMENT '购买数量',
`spec_goods_id` int(11) NOT NULL COMMENT '规格商品id',
`is_selected` tinyint(1) NOT NULL DEFAULT '1' COMMENT '购物车选中状态',
`create_time` int(11) unsigned DEFAULT NULL,
`update_time` int(11) unsigned DEFAULT NULL,
`delete_time` int(11) unsigned DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8;
加入购物车
-
获取数据
-
参数校验
-
添加数据
-
合并购物车数据 在账号登录后调用
- 获取数据
- 参数校验
- 对数据合并
- 存在相同的记录进行累加操作
- 不相同的记录进添加数据
-
更简单的逻辑 加入购物车的时候跳转到登录 让其登录 后在跳转回来
-
这样一个购物车就 数据就不牵涉到数据合并的问题
获取购物车列表数据
- 查询当前的用户的购物车数据
- 查询购物车商品的 失效状态 如果下架了 或者库存不足 都要提示
- 返回给用户
合并购物车数据
- 获取数据
- 参数校验
- 存在相同的记录进行累加操作
- 不相同的记录进添加数据
删除购物车数据
- 删除单个 多个 全部
- 根据参数的不同来执行不同的效果
- 或者写不同的接口来执行不同的效果
- 获取参数 单个 或多个
- 校验 用户购物车中是否有该数据
- 全部 直接操作
- 返回结构
修改购物车商品选中状态
- 单个 或者 全选 反选
- 获取参数 单个
- 校验 用户购物车中是否有该数据
- 返回结构
结算功能
收货地址
表结构
CREATE TABLE `address` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL DEFAULT '0' COMMENT '用户id',
`consignee` varchar(64) NOT NULL DEFAULT '' COMMENT '收货人姓名',
`phone` varchar(20) NOT NULL DEFAULT '' COMMENT '收货人手机号',
`province` int(11) DEFAULT NULL COMMENT '省份编码',
`city` int(11) DEFAULT NULL COMMENT '城市编码',
`district` int(11) DEFAULT NULL COMMENT '县区编码',
`area` varchar(255) DEFAULT NULL COMMENT '省市区名称',
`address` varchar(255) NOT NULL DEFAULT '' COMMENT '详细地址',
`is_default` tinyint(1) DEFAULT '0' COMMENT '是否默认:0否 1是',
`create_time` int(11) unsigned DEFAULT NULL,
`update_time` int(11) unsigned DEFAULT NULL,
`delete_time` int(11) unsigned DEFAULT NULL COMMENT '软删除时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
下订单
每次用户提交订单,需要在订单表添加一条记录,在订单商品关联表添加多条记录。
订单数据处理完成,从购物车表将对应记录删掉。
订单编号:自动生成(数字、字母组合,不能重复)
用户id:读取token来校验
订单金额:计算累加
从结算页需要收集的数据
收货地址address_id
创建订单
向订单表添加一条数据,
向订单商品表添加多条记录,
从购物车表删除对应的记录
①组装订单表需要的数据,添加一条记录到订单表
商品价格计算总价 后端要处理验证
还要查询相关的数据 收货地址
一个订单对应多个商品 将 购物车选中的商品批量添加到订单商品表
选择支付方式
库存
SKU => 库存
①从商品详情页 到 购物车 到结算,都可能需要显示 “有货” “无货”
②创建订单过程中,维护商品的库存
扣减库存:
创建订单时:扣减库存(预扣减|冻结库存):设置支付时间,超过时间未支付,释放预扣减库存。
支付时:支付成功扣减库存,支付失败,释放预扣减库存。
问题:
只在创建订单时扣减库存:如果下订单后不支付,则无法继续销售。
只在支付成功时扣减库存:容易发生超卖现象,用户付款了,没货。
创建订单时预扣库存
创建订单时,对每一个商品,都要检测库存是否充足,如果库存不足,则订单创建失败
库存充足,则预扣库存(冻结库存)
支付接口
支付宝开放平台: https://open.alipay.com
聚合支付解决方案 https://www.pingxx.com/docs/overview/config
聚合支付解决方案
聚合支付,是融合了多方支付通道的平台,相对于支付宝、微信等第三方支付平台而言,聚合支付属于在第三方支付平台基础上,进行了技术融合的第四方平台。
本解决方案,是基于PC网站,集成聚合支付中的扫码支付方式,实现支付宝、微信同码的一种基础方案。
主要实现步骤为:开通聚合支付服务(真实环境需要开通第三方支付渠道),配置开发信息,项目集成。
项目集成又包含:生成支付二维码,扫码支付请求ping++的创建change对象接口、处理异步通知、支付结果轮询等。
ElasticSearch 全文搜索引擎
分布式全文搜索解决方案:是基于Mysql数据库 、 Hadoop生态(可选)、 ElasticSearch搜索引擎三大数据系统实现一个分布式全文搜索系统。
Mysql数据库用于结构化存储项目数据。
Hadoop生态用于备份关系型数据库的所有版本,还存储用户行为,点击,曝光,互动等海量日志数据,用于数据分析处理。
ElasticSearch搜索引擎用于对Mysql或者Hadoop提供的数据进行索引和全文搜索。
其中核心功能,包括全量创建索引、增量创建索引、实时同步数据(文档的curd)、全文搜索等。
tp框架 使用 sqlite
首先php.ini 里面要开启 pdo sqlite 扩展
// 配置
'sqlite' => [
// 数据库类型
'type' => 'sqlite',
// 数据库名 # 这里替换成自己的 SQLite 数据库文件路径
// sqlite: + 数据库文件路径
'dsn' => 'sqlite:C:\web\tp即时通讯\tp\db\wechat.sqlite3',
// 表前缀
'prefix' => 'we_',
// 数据库名
'database' => 'main',
// 默认采用
'charset' => 'utf8',
// 是否启用字段缓存
'fields_cache' => false,
// SQL监听
'trigger_sql' => true,
],
phinx 配置使用 sqlite
# 安装
php composer.phar require robmorgan/phinx
# 初始化
vendor/bin/phinx init
# 需要提前创建对应的目录 要不然会报错
# /db/migrations
# /db/seeds
# 创建迁移脚本
php vendor/bin/phinx create MyNewMigration
# 创建Seed类 数据生成
php vendor/bin/phinx seed:create UserSeeder
# 执行seed
phinx seed:run -e development
# 使用对应的配置 执行迁移文件
phinx migrate -e development
# 回滚
phinx rollback -e development
// phinx.php 使用初始化后会生成对应的文件 修改配置
<?php
return
[
'paths' => [
'migrations' => '%%PHINX_CONFIG_DIR%%/db/migrations',
'seeds' => '%%PHINX_CONFIG_DIR%%/db/seeds'
],
'environments' => [
'default_migration_table' => 'phinxlog',
'default_environment' => 'development',
'production' => [
'adapter' => 'mysql',
'host' => 'localhost',
'name' => 'production_db',
'user' => 'root',
'pass' => '',
'port' => '3306',
'charset' => 'utf8',
],
'development' => [
'adapter' => 'mysql',
'host' => 'localhost',
'name' => 'development_db',
'user' => 'root',
'pass' => '',
'port' => '3306',
'charset' => 'utf8',
],
'testing' => [
'adapter' => 'mysql',
'host' => 'localhost',
'name' => 'testing_db',
'user' => 'root',
'pass' => '',
'port' => '3306',
'charset' => 'utf8',
],
'sqlite' => [
'adapter' => 'sqlite',
// 设置数据库文件路径
'name' => './db/wechat',
// 字符集
'charset' => 'utf8',
],
],
'version_order' => 'creation'
];
使用队列功能
https://packagist.org/packages/topthink/think-queue
安装
composer require topthink/think-queue
配置文件 建议使用redis 别用其他的
// config/queue.php
return [
'default' => 'redis',
];
创建任务类
推荐使用 app\job
作为任务类的命名空间 也可以放在任意可以自动加载到的地方
任务类不需继承任何类,如果这个类只有一个任务,那么就只需要提供一个fire
方法就可以了,如果有多个小任务,就写多个方法,下面发布任务的时候会有区别
每个方法会传入两个参数 think\queue\Job $job
(当前的任务对象) 和 $data
(发布任务时自定义的数据)
还有个可选的任务失败执行的方法 failed
传入的参数为$data
(发布任务时自定义的数据)
列子
// 单任务
namespace app\job;
use think\queue\Job;
class Job1{
public function fire(Job $job, $data){
//....这里执行具体的任务
if ($job->attempts() > 3) {
//通过这个方法可以检查这个任务已经重试了几次了
}
//如果任务执行成功后 记得删除任务,不然这个任务会重复执行,直到达到最大重试次数后失败后,执行failed方法
$job->delete();
// 也可以重新发布这个任务
$job->release($delay); //$delay为延迟时间
}
public function failed($data){
// ...任务达到最大重试次数后,失败了
}
}
// 多个任务
namespace app\lib\job;
use think\queue\Job;
class Job2{
public function task1(Job $job, $data){
}
public function task2(Job $job, $data){
}
public function failed($data){
}
}
发布任务
think\facade\Queue::push($job, $data = '', $queue = null)
和think\facade\Queue::later($delay, $job, $data = '', $queue = null)
两个方法,前者是立即执行,后者是在$delay秒后执行
$job
是任务名
命名空间是app\job
的,比如上面的例子一,写Job1
类名即可
其他的需要些完整的类名,比如上面的例子二,需要写完整的类名app\lib\job\Job2
如果一个任务类里有多个小任务的话,如上面的例子二,需要用@+方法名app\lib\job\Job2@task1
、app\lib\job\Job2@task2
$data
是你要传到任务里的参数
$queue
队列名,指定这个任务是在哪个队列上执行,同下面监控队列的时候指定的队列名,可不填
监听并执行任务
php think queue:listen
php think queue:work
在linux 上 在命令后面 添加 &符合让其进入后台运行