# shop58 **Repository Path**: WFTech578/shop58 ## Basic Information - **Project Name**: shop58 - **Description**: laravel5.8电商项目,功能包括用户中心、收货地址、电商管理后台、权限管理、商品管理、商品 SKU、购物车模块、订单模块、支付模块(支付宝、微信支付)、商品评价、商品收藏、订单退款流程、优惠券模块,技术知识点包括 Laravel 中事务操作(Transaction)、支付接口调试、订单流水号生成、预加载与延迟预加载、事件和监听器、Service 模式、自定义验证器。 - **Primary Language**: PHP - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 3 - **Created**: 2021-05-14 - **Last Updated**: 2021-05-14 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # shop58电商 ## 技巧 - 辅助 - "bootstrap/helpers.php" - `Ramsey\Uuid\Uuid`唯一性识别类 - 类中公共独立不依赖外部数据的方法,用静态方法 - Vscode - 全局搜索 `App\Models\User` - 全局替换 `ctrl+shift+H` - Artisan 命令 - 创建模型,顺便将控制器,模型工厂,迁移文件也建了 - `make:model -a Models/UserAddress` - `factory(App\Models\UserAddress::class,3)->create(['user_id'=>1])` - **自动推断注册策略类** - 类接口与实现 - `Illuminate\Contracts\Auth\Access\Gate` - `Illuminate\Auth\Access\Gate` 实现类 ```php Gate::guessPolicyNamesUsing(function ($class) { return '\\App\\Policies\\' . class_basename($class) . 'Policy'; }); ``` - 路由 - 多重命名 - `Route::redirect('/','/products')->name('root');` - `Route::get('products', 'ProductsController@index')->name('products.index');` - 路由顺序 - *多条路由根据注册顺序匹配,先注册的被匹配到了后续不会再被响应* - 定制异常处理页 - `make:exception InternalException` 创建异常类 - `render` 方法定制错误渲染页信息 - 校验 - validate 如果校验失败就会返回 422 状态码 - `DB::transaction()` 必须有返回值, 外部才能使用其返回的变量传递出来 - 若字段没有在 `fillable` 属性里面,则无法写入。Laravel 的推荐写法就是通过关联关系的 `associate ()` 方法 - `innodb` 默认隔离级别是可重复读 - 请求 - App\Http\Requests\Requset 是 Illuminate\Http\Request 的子类 - 需要校验数据的时候,根据我们的代码规范,是要放在 Request 子类中进行的,不需要数据校验的时候就用 Illuminate\Http\Request - 规则提取 - 将提取指定表字段是否存在,给条件从隐式路由对象模型中提取order对象 - `Rule::exists('order_items', 'id')->where('order_id', $this->route('order')->id),` - 前端某些表单字段向接口请求之后,需要禁用`prop('readonly', true)` - 可以对不同的请求来源,对应不同的响应格式 - load vs with - load() 延迟预加载,在已经查询出来的模型上调用 - with() 则是在 ORM 查询构造器上调用 - 封装业务代码 - 对逻辑复杂的业务代码进行封装 - *注意trait的方法与属性重写,属性直接在trait所在的类内写同名属性报错* - 模型 - 模型状态用常量,处理中文用关联数组(约定用map后缀)映射(键使用`self`) - 在对模型做序列化时,会把 `$appends` 中列出的访问器也序列化进来。 - jquery中遍历跳过 - jQuery.each遍历中,需要跳过某些项继续下次遍历,使用`return` - 通常jQuery变量以`$`开头标识,以示与原生js变量的区别 - 事件监听器 - 可以异步执行(实现标记接口`ShouldQueue`),默认是同步 - 任务天生就是异步执行,需要队列 - 而通知需要借助实现Messages接口的消息对象构建 - **关联属性 VS 关联方法** - $order->items() 是获取关联关系,这个时候还**没有发生 SQL 查询**,通常是准备做进一步的查询 - $order->items 则是获取关联的模型,**SQL 已经执行完毕**,已经从数据库中取到了所有关联的数据。 - DB聚合查询 - `->first([DB::raw('avg(rating) as rating')])` - 级联设空 `->onDelete('set null')` - windows类似grep命令 - `php artisan list | findstr /i queue` - `/i`对大小写不敏感的参数, 默认敏感 - 除了 `findstr` 还有 `find` - Redis - *查阅key,判断key所属数据类型,根据类型查看数据的操作方法* - key 决定了数据索引 - type 数据类型通常决定数据的操作方法 - `keys *` 查看当前库的所有key值 - `type KEY_NAME` 查看key的数据类型 - 可在 `database` 配置中配置redis队列前缀,以免冲突 ## 扩展包 - `overtrue/laravel-lang` - 多语言翻译,对laravel自带的认证脚手架有效,汉化错误信息 - `china-area-data` - 中国区域联动数据包js,在做地址方面很有效,需要注意的港澳台数据要单独处理 - `sweetalert` - 弹窗依赖 - `encore/laravel-admin` - 管理后台默认帐号密码admin - `php artisan vendor:publish --provider="Encore\Admin\AdminServiceProvider"` - `php artisan admin:install` 数据迁移 - 有相关的中文文档 - `php artisan list | grep admin` 查看与后台相关的命令行工具 - 支持blade模板自定义页面,laravel的view方法返回对象作为body方法的参数 - `tooltip` bootsrap插件 - `swal().then(function(willDelete){})` - 用户点击确定 按钮,willDelete 的值就会是 true,否则为 false - `@fortawesome/fontawesome-free` 免费字体文件 - `yansongda/pay` - 库封装了支付宝和微信支付的接口,可定制Route路由 - 阿里需在本地配置应用公钥,配置中的阿里公钥 - 官方的`laravel-pay`只支持在配置中定义回调地址 - `endroid/qr-code` - 二维码生成库,支持二维码存储有限的字符信息 ## 札记 - 接口类与实现类是有区别的,实现类可在接口类上进行扩展一些自己的方法 - 文件配置 - 基本原理,将真实文件存储在storeage文件夹下,配置完毕,必须重新创建符号链接`storage:link` - `filesysems.php`将文件的存储物理路径与访问路径作了映射,url访问路径通常经过storage - 默认情况下, public 磁盘使用 local 驱动,并且将这些文件存储在 storage/app/public 目录下 - 为了使它们能通过网络访问,你需要创建 public/storage 到 storage/app/public 的符号链接 - 目的 - **把可公开访问文件都保留在同一个目录下,以便在不同的部署之间共享这些文件** - 若网络访问需要配置相应的 `url` 项 - 数据库迁移 - 尤其在有外键的时候出错,断开数据库连接释放session,重新来过可解决大部分问题 - 查询搜索 - 前端 - 搜索内容 + 条件 - 内容input,条件用select,整个处于form表单内,用jQuery选中表单名,监听按钮提交 - 后台 - 向模板渲染条件维持数据,使用分页对象的append插入方法 - 对条件进行正则匹配,生成合适的查询语句 - 支付接口 - 一般都有前端与后端回调,需要注意的是后端异步回调路由需要在`VerfyCsrfToken` 中间件内排除 - 退款中状态的必要性,对方回调异步通知 ## 电商业务 - 购物车 - 购物车的数据通常会保存到 Session 或者数据库 - 对于多端电商一般是将购物车保存在数据库 - 注意项 - 使用 Service 模式对业务代码的封装来提高代码的复用性 - 高并发下减商品库存(涉及超卖) - 使用延迟队列自动关闭未支付订单 - 创建订单时应保存用户收货地址的快照而非 ID - 代表状态的值应使用常量 - 订单创建使用数据库事务 - 支付 - 微信与支付宝的**前端回调与后端回调** - 支付宝有沙箱环境 - 微信支付需要一个开通了微信支付的公众号,其申请需要有公司资质,通常是拉取一个支付二维码 - 使用事件及监听器完成了支付后的更新销量与邮件通知 - 自动收货 - 后台发货之后触发一个 30 天的延迟任务,30 天后判断收货状态,如果是未确认收货就自动标记确认收货 - 评分评价传递 - 订单结束对sku的评分评价-> sku的整体评分评价 -> 商品的评分评价计算 - 以订单为入口的sku评分评价 - 以产品为入口的product评分评价 - 退款 - 通常需要给定生成的退款单号,与订单流水号一对一 - 拒绝退款,必须给出退款理由 - 退款状态,本次申请状态无法通过,则回退至上一个状态 - **微信支付的退款结果并不是实时返回的,而是通过退款回调来通知** - *对于回调的退款,因不确定对方何时回调执行成功,因此需要一个退款处理中的状态来表示* - 凡是外部请求服务器的,需在csrfToken中间件内配置例外验证属性 ```php // 规则示例 'agree'=>['required','boolean'], // 条件必选规则 拒绝退款时需要输入拒绝理由 'reason'=>['required_if:agree,false',] ``` - 根据支付宝的文档,若返回值内有`sub_code`字段说明退款失败 - 优惠券 - 固定优惠金额,满减 - 抵扣,百分比折扣,有条件的最低消费,满百分比折扣 - 没有填写开始时间和结束时间,就表示优惠卷可以随时使用 - 关闭的订单任务,若订单中使用了优惠券,在执行关闭任务时,需要将其退还到增加优惠券池中。 - **关闭未支付订单** - 理由:恶意用户可以通过下大量的订单又不支付来占用商品库存,让正常的用户因为库存不足而无法下单 - 当创建订单之后一定时间内没有支付,将关闭订单并退回减去的库存,需要用到计划任务,队列 - 应用场景在创建订单后,加入队列调度器,在任务逻辑区判断订单是否支付,未支付则关闭订单,同时恢复sku库存 - 启动队列处理器 `php artisan queue:work` - 可指定连接与队列 `->onConnection('sqs')->onQueue('processing')` - 指定监听特定的连接(连接通常配置库)与队列(即其它数据库中的表) - `php artisan queue:work redis --queue=emails` - `queue:restart` 来优雅地重启队列进程 - SerializesModels 这个 Trait,会在序列化时把模型的 ID 存起来而不是把整个模型存起来 -** 在执行 Job 的时候会根据id从数据库重新读取相应模型,故创建 Job 传入时的模型数据与执行时不一定一样**。 - Laravel 5.5 及以上版本已经不用 `queue:listen` ` - 完善支付后逻辑 - 事件 - 更新产品销售数据 - 发送订单已支付事件监听->邮件通知