多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
json() 提供 方法 修改 开启报错查看 ![](https://img.kancloud.cn/0c/90/0c9007f59b7ad18eeefe50fc4238e4ff_196x109.png) ## env 中开启 de\_bug database配置数据库 会先使用 .env 中的配置! ![](https://img.kancloud.cn/ee/ad/eead3bc36f4c4c5fafb7666be0f5d46c_885x660.png) ![](https://img.kancloud.cn/39/95/399564537aa41bef8a6697a4a2fd28c8_459x495.png) tp6 中使用 **Db**:: 需要 use 门面模式 user think\\facade/\\Db ![](https://img.kancloud.cn/b5/7c/b57cde7c1e3dd0ba472045914387d9b9_1073x294.png) ## app.php config/app.php 错误信息 ## 多应用模式 ``` composer require topthink/think-multi-app ``` 在当前控制器下创建 route 路由文件夹 配置域名访问 http://www.tp6.com/demo.php/index/test?a=1 在public 下复制index.php 改名为 demo.php $response = $http->name(‘demo’)->run(); 若是文件名不同 可用 name指向 config 可以在每个 目录下 + admin / config 当前目录有用 api/config … ## 路由 多应用 需要 加 文件名 admin/text index/… ## config 下 业务状态码 创建 status.php` return \[ ‘success’ => 1, ‘error’ => 0, \]; 未开启强制路由 都可以 http://www.tp6.com/index.php/demo/index/t demo 应用 inde想控制器 t方法 http://www.tp6.com/demo.php/index/t ![](https://img.kancloud.cn/b7/07/b707c6984f257935eca4e4625cfe7788_1018x521.png) ![](https://img.kancloud.cn/2a/42/2a42dbffa035fae58125f5760e476801_926x407.png) ## has 判断某个值是否设置 ## 容器和依赖注入 ![](https://img.kancloud.cn/3b/5a/3b5a3ab7606f10135a50ec4d200454c0_654x276.png) ![](https://img.kancloud.cn/d4/b7/d4b7603cc4f8e8488f67df688518e459_368x390.png) ## 全局中间件 可以通过命令行指令快速生成中间件 php think make:middleware Check ~~~ <?php namespace app\middleware; class Check { public function handle($request, \Closure $next) { // 添加中间件执行代码 ... return $next($request); } } ~~~ 在 app/middleware.php ~~~ <?php // 全局中间件定义文件 return [ // 全局请求缓存 // \think\middleware\CheckRequestCache::class, // 多语言加载 // \think\middleware\LoadLangPack::class, // Session初始化 // \think\middleware\SessionInit::class app\middleware\Check::class, ]; ~~~ ## 应用中间件 ![](https://img.kancloud.cn/89/f2/89f28316c52f626c45143d902170f7b8_302x378.png) ## 模板引擎 composer安装 ![](https://img.kancloud.cn/1e/ad/1eadbadf346ddd4716c0c47f6de043e3_1106x700.png) ``` composer require topthink/think-view ``` ## 简单的密码+盐处理 密码 123 md5(‘123\_md5’) 处理字符串 ## 验证码 ``` composer require topthink/think-captcha ``` ![](https://img.kancloud.cn/a0/aa/a0aa90a524ece726e29d54f48b01bb8b_1851x1080.png) ![](https://img.kancloud.cn/4e/9e/4e9ed419d414fdb8f7bf8a78f927f6f0_1089x236.png) 验证码使用 验证是时 徐快需要在中间件中开启session! ![](https://img.kancloud.cn/54/3e/543eac0212132ea796af2bdc73167085_1851x1080.png) ## 模型分层架构 ![](https://img.kancloud.cn/12/78/1278c00aeef4e02ce8fd4937ef5a0c1d_1306x521.png) 思维 ## 验证器 ~~~ $data = [ 'username' => $username, 'password' => $password, 'captcha' => $captcha, ]; $validate = new \app\admin\validate\AdminUser(); if(!$validate->check($data)) { return show(config("status.error"), $validate->getError()); } ~~~ ~~~ <?php /** * Created by singwa * User: singwa * motto: 现在的努力是为了小时候吹过的牛逼! * Time: 07:11 */ namespace app\admin\validate; use think\Validate; class AdminUser extends Validate { protected $rule = [ 'username' => 'require', 'password' => 'require', 'captcha' => 'require|checkCapcha', ]; protected $message = [ 'username' => '用户名必须,请重新输入', 'password' => '密码必须', 'captcha' => '验证码必须', ]; protected function checkCapcha($value, $rule, $data = []) { if(!captcha_check($value)) { return "您输入的验证码不正确!"; } return true; } } ~~~ ## Url 注意 ![](https://img.kancloud.cn/e6/15/e615c34cdf3dfbc35db4ea0d9f62b741_557x165.png) ## 如何正确分层之操作datebase ## 注意使用 init初始化 ## 如何处理在init里无法重定向 ![](https://img.kancloud.cn/83/c0/83c0606e76437160937177719eea752b_1851x1080.png) ## 注意 如何 /admin/index/index 写的时候需要+ /’ ## 前置中间件 ![](https://img.kancloud.cn/4d/de/4ddedaf37985db4e6bbaab15ad8689f7_1851x1080.png) preg\_match() 1 正则 2 str 匹配2中有无 1 return 1 0 ## 后置中间件 ![](https://img.kancloud.cn/f9/10/f910186efb51f5a31788f97233776d80_789x735.png) ## session ~~~ session('adminUser', $res); session('adminUser', null); ~~~ * 1 ## business 业务逻辑层 在business 处理业务逻辑 -> 调用 model 的sql执行 在business进行 异常抛出 在c层try catch 补获 model 放在 common 公共文件里 ![](https://img.kancloud.cn/d8/be/d8be27a60a09cb14a946e5b53725a2fd_1851x1080.png) ## redis 实现验证码 存活时间60s 安装 https://blog.csdn.net/kxukai/article/details/106692983 ## 日志 开启日志 runtime 可以查看执行的原生sql语句 进行分析 ![![](https://img.kancloud.cn/d8/be/d8be27a60a09cb14a946e5b53725a2fd_1851x1080.png)](images/screenshot_1653443315790.png) ## 工厂模式 ![](https://img.kancloud.cn/21/08/2108d204a41d287191e7460f13dcf0cc_1850x1080.png) ![](https://img.kancloud.cn/da/56/da565da7d763838acef7e3260b8f689c_1850x1080.png) ![](https://img.kancloud.cn/23/e1/23e1f0e4616fd9cb9b87d27be00f1346_1850x1080.png) 短信可以使用redis 进行限制 存手机号 + 发信息间隔时间~ ## 如何流控 20%阿里云短信 80%百度云短信 案例:2018年年底 央视春晚 百度手机号登录发送验证码分发不同运营商流量, 就是用这个来做的, 并发50万 发送短信验证码。 简单粗暴 实用 最简单的方法往往最有效 ~~~ $a = rand(0,99); if($a < 80) { // 阿里云逻辑 } else { // 百度云逻辑 } ~~~ ## 前后端分离 redis+ token 不使用session+cookie ## config(‘statis.success’) ![](https://img.kancloud.cn/27/86/278635f2adffa5b34a7b9ca052a25e43_475x395.png) ## 异常处理 try catch 捕获异常时 注意记录日志 以供分析 tp6 helper 全局搜索自带抛出异常 throw\_if 同laravel 手动抛出异常 ~~~ throw new \think\Exception("不存在该验证码", config('status.code.not_code')); ~~~ * 1 捕获异常 ~~~ try { $result = (new \app\common\business\User())->login($data); } catch (\Exception $e) { return show($e->getCode(), $e->getMessage()); } ~~~ // 阻止数据库抛出异常 被 用户看到 手动抛出~ ~~~ try { $this->userObj->save($userData); $userId = $this->userObj->id; }catch (\Exception $e) { throw new \think\Exception("数据库内部异常"); } ~~~ ![](https://img.kancloud.cn/2f/ee/2feece55d1a07376a775a1a7cfffa41a_1851x1080.png) ## redis 删除 等于null 即可 ## redis 写登录 逻辑 前后端分离 所以是 api 接口 1. 用户点击发送验证码 把 sms . 手机号 存入 redis 内容有 验证码 存活60s //这里sms为了再redis中进行区分别的缓存 2. 接收数据 转换格式 验证 isajax ispost ~~~ if (!$this->request->isPost() || !$this->request->isAjax() ) return show(config("status.error"), "非法请求"); $phoneNumber = input("phone_number",'','trim'); $code = input("code", 0, "intval"); ~~~ 3. validate 验证器 验证参数 4. 验证 用户输入的 code !== redis中的该手机号对应的 code ~~~ // 用户输入的 code !== redis中的该手机号对应的 code $redisCode = cache(config("redis.code_pre") . $data['phone_number']); if (empty($redisCode) || $redisCode != $data['code']) { throw new \think\Exception("不存在该验证码", config('status.code.not_code')); } ~~~ 从第五层开始要在 business 逻辑层进行处理 判断 例子 抛出异常最好用 try catch 进行补获 避免数据库抛出暴露信息被用户查看 ![](https://img.kancloud.cn/4c/a3/4ca31f6f7c8d6d7e08df08dbf039e87f_1851x1080.png) 5\. 还是在common/ business 操作 根据用户手机号 查找有该用户登录记录 ~~~ // 需要去判断表 是否有 用户记录 phone_number // 生成token $user = $this->userObj->getUserByPhoneNumber($data['phone_number']); ~~~ 这里model 层在 common/model datebase 操作公共的 可以共用 ![](https://img.kancloud.cn/f2/b6/f2b618a9d4fb46ea7132573341cce74f_574x296.png) 然后进行判断 操作用户记录 ![](https://img.kancloud.cn/a7/14/a71433e0a3f3640d8a3b18f247815495_701x570.png) 6\. 然后把生成的token作为k 用户的id 和用户名作为 v 存入redis 存活时间 ~ ~~~ $redisData = [ "id" => $userId, "username" => $username, ]; $res = cache(config("redis.token_pre") . $token, $redisData, Time::userLoginExpiresTime($data['type'])); ~~~ 7.最后把前端需要的数据返回 ## 中间件 进行 前端 检测是否登录 2种方法 第一种方法 需要验证login的controller extends 该控制器 进行检测 ~~~ class AuthBase extends ApiBase { public $userId = 0; public $username = ""; public $accessToken = ""; public $isLogin = 1; public function initialize() { parent::initialize(); // TODO: Change the autogenerated stub // if ($this->isLogin == 1) { // $this->userId = 6; // 测试场景 // return true; // } $this->accessToken = $this->request->header("access-token"); if (!$this->accessToken || !$this->isLogin()) { return $this->show(config("status.not_login"), "没有登录"); } } /** * 判断用户是否登录 * @return bool */ public function isLogin() { $userInfo = cache(config("redis.token_pre") . $this->accessToken); if (!$userInfo) { return false; } if (!empty($userInfo['id']) && !empty($userInfo['username'])) { $this->username = $userInfo['username']; $this->userId = $userInfo['id']; return true; } return false; } } ~~~ 第二种方法使用中间件 进行检测 在api 文件下 创建一个 ![](https://img.kancloud.cn/6e/c6/6ec69c3a1f816ea28fc424a2b0231d22_523x570.png) 修改 middlevare.php 中 对应信息 ~~~ <?php declare (strict_types=1); namespace app\api\middleware; class Auth { /** * 处理请求 * * @param \think\Request $request * @param \Closure $next * @return Response */ public function handle($request, \Closure $next) { //前置中间件 // if (empty(session('adminUser')) && !preg_match("/login/", $request->pathinfo())) { // return redirect((string)url('login/index')); // } //ajax 返回api格式 例如处理rbac //这样会处理所有api下的控制器 所以需要 // return $this->show(config("status.not_login"), "没有登录"); if (in_array($request->pathinfo(), config('login.need_verify_login'))) if (!$this->isLogin()) return ajaxReturn(0, '', '未登录,请先登录'); return $next($request); } public function end(\think\Response $response) { } /** * 判断用户是否登录 * @return bool */ public function isLogin() { $token = request()->header("access-token"); $userInfo = cache(config("redis.token_pre") . $token); if (!$userInfo) { return false; } if (!empty($userInfo['id']) && !empty($userInfo['username'])) { return true; } return false; } } ~~~ ## 免登录 打开登录界面 前 执行控制器 的 中间件 进行 验证 获得返回数据 进行 重定向至跳转首页 or 等待用户登录 ##退出登录 清空当前redis中 k 拼接 对应的token v 为 null ![](https://img.kancloud.cn/aa/f1/aaf15929fd635a738c52016a46b4775a_1174x404.png) ## model 层代码优化 business 逻辑层 ! ![](https://img.kancloud.cn/33/f7/33f78f9c524360383688d951b6f02653_1003x630.png) model 层![](https://img.kancloud.cn/ff/83/ff835a3a3e362e06d4d55abcc9fe7252_1150x518.png) 这里可以父类继承 调用 减少代码量 ![](https://img.kancloud.cn/c1/18/c118236b54088a2b283e75731b41a5f1_1851x1080.png) ## 面向过程 => 面向对象 统一返回给前端 ![](https://img.kancloud.cn/be/e5/bee571a2d1d8217d2a80380ff7dbd85a_1920x1051.png) ## 模型 使用 ![](https://img.kancloud.cn/35/4c/354cc5e31561dd186d7eb954c9fb0475_1138x356.png) with 会 `两次`执行sql语句 进行查询 数据量大 大公司常用 withjoin 一次join 查询 ## sku 建议用2 方便 直接对应sku表的id 统一规格也使用 sku ![](https://img.kancloud.cn/8b/3b/8b3b440c434659ec6475445c5a28ab72_1066x479.png) ## 系统浏览量 pv 每个商品的浏览量 ![](https://img.kancloud.cn/a7/72/a77279101a4a8859f69b7a0e4582b564_866x202.png) ## 商品展示流程 展示商品有自己默认skuid ![](https://img.kancloud.cn/94/08/9408a809ad89a3107b74b4db17436767_893x633.png) 用户点击根据skuid 到sku查询到 该商品的goodsid 然后根据goodsid 到goods 获取该商品信息 和 该商品对应的 所有 sku信息 ![](https://img.kancloud.cn/5b/97/5b979c0639c18009b53904d2a5157be0_736x412.png) ![](https://img.kancloud.cn/82/58/82585a710f1162734df4ee6aaf67a804_721x277.png) 这里gids 1,11 :1 : 1 //skuid 1, 11 规格id // 颜色, 尺码 ![](https://img.kancloud.cn/1f/e2/1fe2ea2668cdea95d3d3a82d7aa58f26_355x174.png) ![](https://img.kancloud.cn/3a/00/3a00791acdb9c137357ee2db1f1b18ec_268x371.png) ![](https://img.kancloud.cn/b0/c8/b0c8cd1c9c7671fff4c936a51fceb1b7_1177x665.png) sku ![](https://img.kancloud.cn/23/6e/236e291e7ecfad891de0a843a8d3054b_347x561.png) ## 购物车 浏览器缓存 mysql redis 存redis 高性能 hash 表如何设计的 流程 1.加入购物车 用户登录状态 点击商品加入购物车 即加入redis 中 (1) 到mysql根据skuid查找该商品的sku数据 2.购物车展示页面 3.购物车删除 修改数量 4.购物车排序 ![](https://img.kancloud.cn/4b/0d/4b0d6d28010f5914872adc025cf27d37_1920x1051.png) ![在这里插入图片描述](https://img-blog.csdnimg.cn/2020062411424451.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2t4dWthaQ==,size_16,color_FFFFFF,t_70) ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200624114310976.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2t4dWthaQ==,size_16,color_FFFFFF,t_70) 封装在基础类库 hgetall ![](https://img.kancloud.cn/97/55/97551e20b850e54f0e8b470fa53214cb_1037x709.png) 删除 hdel test 1 2 ![](https://img.kancloud.cn/17/e5/17e5243b70884df6b208f8ff249f29ac_842x471.png) 获取redis 购物车数据 进行排序 ![](https://img.kancloud.cn/3f/f3/3ff363dc9a276dff49e83d59678527dc_795x217.png) hLen 获取数据 返回值 integer-reply: 哈希集中字段的数量,当 key 指定的哈希集不存在时返回 0 例子 redis> HSET myhash field1 “Hello” (integer) 1 redis> HSET myhash field2 “World” (integer) 1 redis> HLEN myhash (integer) 2 redis> ## 库存 严谨判断 购物车+ 订单 未支付 …2 hMget 返回 key 指定的哈希集中指定字段的值。 对于哈希集中不存在的每个字段,返回 nil 值。 hGetAll 返回 key 指定的哈希集中所有的字段和值。 ![](https://img.kancloud.cn/a3/b7/a3b7792b6171c85788c6b2c3bf48e2d3_1071x519.png) ![](https://img.kancloud.cn/17/00/17005dbfaf8e1f54de8de31de9bbf38d_1371x485.png) 订单 主表副表!![](https://img.kancloud.cn/e6/e8/e6e878f30421a1b7f43865cb270c51ef_1422x535.png) ![](https://img.kancloud.cn/91/6a/916afebb43cc0478c1d8bea810cab10e_1138x298.png) ## 订单流程 进入 加入 购物车页面 订单确认页面 和 提交订单页面 都需要判断 `库存` 提交订单流程 1. 删除购物车 的该商品 2. 减库存 3. insert order 主表 副表 ## redis 延迟队列处理无效订单 用户点击 确定订单时 未支付 存 redis 20分钟有效期 然后脚本每秒 和当前时间比较 小于当前时间的则 把该订单状态改为 已取消 恢复该订单对应sku的库存 存redis 订单id time()+20min ![](https://img.kancloud.cn/11/60/1160f71b074baff581fb47bbedd2519b_930x491.png) ## 定时任务 ![](https://img.kancloud.cn/93/0b/930bd8479b6e77efecc6a253586ba301_945x700.png) ![](https://img.kancloud.cn/51/42/514256597f3978a22c8c82ecef2511cc_915x540.png) ![](https://img.kancloud.cn/93/fd/93fd21cdafba5d5d997f599f3b4dab79_1095x782.png) 暂时有bug 生成 所以要手动挡 ![](https://img.kancloud.cn/64/df/64dfc9dc4d62f6742094a9429e37d836_495x798.png) 最后在当前tp6 目录的 命令行 运行 php think order 即可开启进程 如果定时任务 进程挂了这么解决? supervise维持进程任务自动重启 ## 支付抽离 单独服务 子服务 微服务 ## 写出用户所有流程 支付流程 从购物车点击进入结算 下单页 先减库存 点击立即付款 跳转支付界面 然后看支付回调 成功 失败 进行数据库处理 库存等等… ## Start 前置中间件 检测 是否登录 || 创建一个base来继承 在其中判断 pathinfo 对比 判断前端传来的token 是否存在redis 登录 注册 用redis存储60s 验证码 进行判断 sms . 133333333 验证码 | sms 是标识 拼接手机号 作 k v是验证码 参3存活时间 登录成功 redis 存储 k => user\_token . 后台生成唯一token v => 登录的用户信息 然后把token 给header头 进首页 验证 注册登录 1. 登录流程 验证登录 购物车 订单 支付