Blogger Information
Blog 57
fans 3
comment 0
visits 60367
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
商城项目-后台商品发布/前端页面引入/前端用户登录验证中间件/生成订单
岂几岂几
Original
1769 people have browsed it

商城项目-后台商品发布/前端页面引入/前端用户登录验证中间件/生成订单

学习心得

  • 要学习朱老师用”模块化”的思想编写前端页面组件, 实现代码复用.

  • laravel的用户验证, 西门老师只领进门, 更多使用还得看手册.

  • 巧妙的添加查询条件, 有效的解决大量数据的读取的效率问题.

1. 后台商品发布/前端页面引入

  • 1.1 发布商品时, 如何加载商品分类

    • 之前老师介绍过, 为消灭关联查询, 把要关联的表, 以 id 做数组 key 的方式查询出来. 这种方式, 对要关联的表, 数据量少的时候, 可以适用. 当数据量很多时, 比如, 淘宝的商品分类表, 就有好几百兆的数据, 没有必要全查出来.
    • 为解决这个问题, 可以在查询出商品分页数据后, 把这些分页商品的分类id做成in参数, 在查询以id做数据key的数组时, 适用in进行过滤, 这样可以减少查询时间消耗.
    • 在新增商品界面, 如果要加载商品分类, 假设商品分类很多, 可以加一个搜索框, 通过搜索框输入的内容异步的去过滤查询商品分类项, 返回的数据拼接字符串形成 <select> 元素的 <option> 列表并渲染即可. 记得最后要 form.render('select') .
      • 模糊查询要用 like , 当分类很多时, 数据库是撑不住的.此时就需要用搜索引擎了, 如: sphinx , elasticsearch .
      • 模糊查询, 可能会匹配到很多数据, 所以查询一定要加限制返回的行数, 像百度搜索 laravel文档 , 其实也只返回权重较高的730条数据而已, 但匹配到的数据上百万条.
  • 大量数据的的读取方式

    • 启用缓存. 如Redis, memerycache等. 先从缓存中读取数据, 如果读取不到, 再冲数据库中获取, 然后保存到缓存中.
  • 1.2 前端页面引入

    • 把共通的部分分离出来, 如头部导航, 底部版权信息等, 使用 blade 引擎的 @include 引入.
    • css也可以使用@import ‘css_url’的方式引入前端模块的css.

2. 前端用户登录验证中间件

  • 2.1 创建前端登录验证中间件

    • 老师总结的三步骤: 创建, 注册和触发.
  • 2.2 使用laravel提供的验证机制进行前端用户登录验证

      1. 照葫芦画瓢, 仿照 User 模型创建一个 Member 模型, 绑定数据库中的前端用户表 member .
      1. 先不用理解, 照老师的讲解, 在 /config/auth.php 中照葫芦画瓢创建一个”防守人”( guard )
      1. 实现前端登录验证中间件并注册之. 在其 handle 方法中, 使用 Auth::guard('member')->guest() 执行登录验证. 使用 guard() 函数指定”防守人”, 如果不调用函数, 则使用laravel默认的”防守人”( user ). guest() 方法则是执行登录验证. 未登录则返回 true .
      1. 同样, 在路由中需要使用前台登录验证中间件的路由上调用 middleware() 方法保护路由.
      1. 如果是前后端分离的项目, 假设项目前端和后端不在同一个域名/服务器中, 因为 session 不能跨服务器共享的原因, 可以改用 /config/auth.php 中声明的另一种驱动方式(对应路由: api.php ): driver => 'token . 具体使用方法要去看文档.
  • tips: 多表更新/插入操作, 是否需要加事务

    • 理论上是要加的, 但会损失部分的性能

    • 但是在实际开发中, 如果订单表(插入)和产品表(减库存)不在同一个数据库服务器, 会比较麻烦, 比较难处理, 索性不加.

  • 生成订单

    • 使用 Auth::guard('member')->user() 获取前端用户信息.

    • 价格是很敏感的字段值, 前端传入的价格不可信, 必须根据商品id从数据库中查询价格.

    • 生成不重复的订单号的方法: 时间戳+用户id+随机数1+随机数2生成订单号, 这样拼接成的订单号出现重复号的概率很低了.

代码清单(部分)

  • 1- 新增商品界面(关注点: 商品分类筛选)
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>新增产品</title>
  7. <link rel="stylesheet" href="/static/plugin/layui/css/layui.css">
  8. <script src="/static/plugin/layui/layui.js"></script>
  9. <!-- ueditor-start -->
  10. <!-- 配置文件 -->
  11. <script type="text/javascript" src="/static/plugin/ueditor/ueditor.config.js"></script>
  12. <!-- 编辑器源码文件 -->
  13. <script type="text/javascript" src="/static/plugin/ueditor/ueditor.all.js"></script>
  14. <!-- ueditor-end -->
  15. <link rel="stylesheet" href="/static/css/product.css">
  16. <style>
  17. .hide {
  18. display: none;
  19. }
  20. #cwords, .cid {
  21. margin-left: 10px;
  22. }
  23. </style>
  24. </head>
  25. <body>
  26. <div class="layui-form">
  27. @csrf
  28. <div class="layui-form-item">
  29. <label for="title" class="layui-form-label">产品名称</label>
  30. <div class="layui-input-block">
  31. <input type="text" class="layui-input" name="title" id="title" value="">
  32. </div>
  33. </div>
  34. <div class="layui-form-item">
  35. <label for="cid" class="layui-form-label">产品分类</label>
  36. <div class="layui-input-inline">
  37. <input type="text" class="layui-input" name="cwords" id="cwords" oninput="search_cates()">
  38. </div>
  39. <div class="layui-input-inline cid">
  40. <select name="cid" id="cid"></select>
  41. </div>
  42. </div>
  43. <div class="layui-form-item">
  44. <label for="thumb" class="layui-form-label">缩略图</label>
  45. <div class="layui-input-block">
  46. <button type="button" class="layui-btn" id="upload">
  47. <i class="layui-icon">&#xe67c;</i>上传图片
  48. </button>
  49. </div>
  50. </div>
  51. <div class="layui-form-item">
  52. <label for="subtitle" class="layui-form-label">子标题</label>
  53. <div class="layui-input-block">
  54. <input type="text" class="layui-input" name="subtitle" id="subtitle" value="">
  55. </div>
  56. </div>
  57. <div class="layui-form-item">
  58. <label for="keywords" class="layui-form-label">关键字</label>
  59. <div class="layui-input-block">
  60. <input type="text" class="layui-input" name="keywords" id="keywords" value="">
  61. </div>
  62. </div>
  63. <div class="layui-form-item">
  64. <label for="descs" class="layui-form-label">描述</label>
  65. <div class="layui-input-block">
  66. <input type="text" class="layui-input" name="descs" id="descs" value="">
  67. </div>
  68. </div>
  69. <div class="layui-form-item">
  70. <label for="price" class="layui-form-label">单价</label>
  71. <div class="layui-input-block">
  72. <input type="number" name="price" id="price" class="layui-input" value="0">
  73. </div>
  74. </div>
  75. <div class="layui-form-item">
  76. <label for="stock" class="layui-form-label">库存</label>
  77. <div class="layui-input-block">
  78. <input type="number" name="stock" id="stock" class="layui-input" value="0">
  79. </div>
  80. </div>
  81. <div class="layui-form-item">
  82. <label for="pv" class="layui-form-label">浏览量</label>
  83. <div class="layui-input-block">
  84. <input type="number" name="pv" id="pv" class="layui-input" value="0">
  85. </div>
  86. </div>
  87. <div class="layui-form-item">
  88. <label for="status_0" class="layui-form-label">商品状态</label>
  89. <div class="layui-input-block">
  90. <input type="radio" name="status" id="status_0" value="0" title="未上架" checked>
  91. <input type="radio" name="status" id="status_1" value="1" title="已上架">
  92. </div>
  93. </div>
  94. <div class="layui-form-item">
  95. <label for="content" class="layui-form-label">商品描述</label>
  96. <div class="layui-input-block">
  97. <!-- 加载编辑器的容器 -->
  98. <!-- 实际生成的是div. 给div添加contenteditable="true", 这个div就能编辑 -->
  99. <script id="container" name="content" type="text/plain">这里写你的初始化内容</script>
  100. </div>
  101. </div>
  102. </div>
  103. <div id="tong" class="hide">
  104. <img src="" style="max-width: 100%; max-height: 100%">
  105. </div>
  106. </body>
  107. <script>
  108. layui.use(['layer', 'form', 'upload'], function() {
  109. layer = layui.layer;
  110. form = layui.form;
  111. upload = layui.upload;
  112. $ = layui.jquery;
  113. //执行实例
  114. var uploadInst = upload.render({
  115. elem: '#upload' //绑定元素
  116. , url: '/admin/upload/pic_upload' //上传接口
  117. , multiple: true // 多文件上传
  118. , data: {
  119. _token: $('input[name="_token"]').val()
  120. },
  121. done: function(res) {
  122. //上传完毕回调
  123. $('<img name="preview_img" src="' + res.data.src + '" alt="" style="height: 36px; margin-right: 5px;" onclick="big_img(this)">').appendTo($('#upload').parent());
  124. },
  125. error: function() {
  126. //请求异常回调
  127. }
  128. });
  129. // 实例化编辑器
  130. /* 第二个参数是配置属性对象 */
  131. /* 不要写var, 把ue声明为全局变量, 因为其他地方也用到ue对象 */
  132. /* var */
  133. ue = UE.getEditor('container', {
  134. initialFrameWidth: '100%', //初始化编辑器宽度,默认1000
  135. initialFrameHeight: '500' //初始化编辑器高度,默认320
  136. });
  137. });
  138. function save() {
  139. var data = {};
  140. data._token = $('input[name="_token"]').val();
  141. data.title = $.trim($('#title').val());
  142. data.cid = $('#cid').val();
  143. data.subtitle = $.trim($('#subtitle').val());
  144. data.keywords = $.trim($('#keywords').val());
  145. data.descs = $.trim($('#descs').val());
  146. data.price = parseInt($('#price').val());
  147. data.stock = parseInt($('#stock').val());
  148. data.pv = parseInt($('#pv').val());
  149. data.status = $('input[name="status"]:checked').val();
  150. data.content = ue.getContent();
  151. // 缩略图
  152. var thumbs = [];
  153. $('img[name="preview_img"]').each(function(index, item) {
  154. thumbs.push(item.src);
  155. });
  156. data.thumb = thumbs.join(',');
  157. // 必填项判空
  158. var check = inputCheck(data, {title: '标题', cid: '商品分类', keywords: '关键字', descs: '描述', price: '单价', content: '商品描述'});
  159. if(check.status) {
  160. return layer.alert(check.message, {icon: 2});
  161. }
  162. // 两个数字域的值判断
  163. if(isNaN(data.price)) {
  164. return layer.alert('单价必须填数字', {icon: 2});
  165. }
  166. if(isNaN(data.pv)) {
  167. return layer.alert('浏览量必须是数字', {icon: 2});
  168. }
  169. // 提交保存
  170. $.post(
  171. '/admin/product/save'
  172. , data
  173. , function(res) {
  174. if(res.status != undefined && res.status == '0') {
  175. layer.msg(res.message, {icon: 1});
  176. setTimeout(() => {
  177. window.parent.location.reload();
  178. }, 1000);
  179. } else if(res.status != undefined) {
  180. layer.alert(res.message, {icon: 2});
  181. } else {
  182. layer.alert('提交保存失败', {icon: 2});
  183. }
  184. }
  185. , 'json'
  186. );
  187. }
  188. // 判空
  189. function inputCheck(data, checkItem) {
  190. for(key in checkItem) {
  191. if(data[key] == undefined || data[key] == '') {
  192. $('input[name="'+key+'"]').focus();
  193. return {status: 1, message: checkItem[key] + '不能为空'};
  194. }
  195. }
  196. return {status: 0};
  197. }
  198. // 商品分类关键字搜索
  199. function search_cates() {
  200. var search_words = $.trim(event.target.value);
  201. if(search_words == '') return;
  202. $.get(
  203. '/admin/product/search_product_cate'
  204. , {words: search_words}
  205. , function(res) {
  206. if(res.status != undefined && res.status == 0) {
  207. var str = '<option></option>';
  208. res.data.forEach(function(item, index) {
  209. str += '<option value="'+item.id+'">'+item.title+'</option>'
  210. });
  211. $('#cid').html('');
  212. $(str).appendTo('#cid');
  213. form.render('select');
  214. }
  215. }
  216. , 'json'
  217. );
  218. }
  219. // 查看大图
  220. function big_img(img) {
  221. if (img.src == undefined || img.src == "") {
  222. return;
  223. }
  224. $('#tong > img').attr('src', img.src);
  225. //页面层-图片
  226. layer.open({
  227. type: 1,
  228. title: false,
  229. closeBtn: 0,
  230. area: ['auto'],
  231. skin: 'layui-layer-nobg', //没有背景色
  232. shadeClose: true,
  233. content: $('#tong')
  234. });
  235. }
  236. </script>
  237. </html>
  • 2- Member模型
  1. <?php
  2. namespace App;
  3. use Illuminate\Contracts\Auth\MustVerifyEmail;
  4. use Illuminate\Foundation\Auth\User as Authenticatable;
  5. use Illuminate\Notifications\Notifiable;
  6. class Member extends Authenticatable
  7. {
  8. use Notifiable;
  9. // 绑定的数据库表名
  10. protected $table = 'member';
  11. /**
  12. * The attributes that are mass assignable.
  13. *
  14. * @var array
  15. */
  16. protected $fillable = [
  17. 'name', 'email', 'password',
  18. ];
  19. /**
  20. * The attributes that should be hidden for arrays.
  21. *
  22. * @var array
  23. */
  24. protected $hidden = [
  25. 'password', 'remember_token',
  26. ];
  27. /**
  28. * The attributes that should be cast to native types.
  29. *
  30. * @var array
  31. */
  32. protected $casts = [
  33. 'email_verified_at' => 'datetime',
  34. ];
  35. }
  • 3- /config/auth.php中增加”防守人”配置
  1. <?php
  2. return [
  3. /*
  4. |--------------------------------------------------------------------------
  5. | Authentication Defaults
  6. |--------------------------------------------------------------------------
  7. |
  8. | This option controls the default authentication "guard" and password
  9. | reset options for your application. You may change these defaults
  10. | as required, but they're a perfect start for most applications.
  11. |
  12. */
  13. 'defaults' => [
  14. 'guard' => 'web',
  15. 'passwords' => 'users',
  16. ],
  17. /*
  18. |--------------------------------------------------------------------------
  19. | Authentication Guards
  20. |--------------------------------------------------------------------------
  21. |
  22. | Next, you may define every authentication guard for your application.
  23. | Of course, a great default configuration has been defined for you
  24. | here which uses session storage and the Eloquent user provider.
  25. |
  26. | All authentication drivers have a user provider. This defines how the
  27. | users are actually retrieved out of your database or other storage
  28. | mechanisms used by this application to persist your user's data.
  29. |
  30. | Supported: "session", "token"
  31. |
  32. */
  33. 'guards' => [
  34. 'web' => [
  35. 'driver' => 'session',
  36. 'provider' => 'users',
  37. ],
  38. 'api' => [
  39. 'driver' => 'token',
  40. 'provider' => 'users',
  41. 'hash' => false,
  42. ],
  43. // 照葫芦画瓢, 增加一个供前端登录验证中间件使用的防守人
  44. 'member' => [
  45. 'driver' => 'session',
  46. 'provider' => 'members',
  47. ]
  48. ],
  49. /*
  50. |--------------------------------------------------------------------------
  51. | User Providers
  52. |--------------------------------------------------------------------------
  53. |
  54. | All authentication drivers have a user provider. This defines how the
  55. | users are actually retrieved out of your database or other storage
  56. | mechanisms used by this application to persist your user's data.
  57. |
  58. | If you have multiple user tables or models you may configure multiple
  59. | sources which represent each model / table. These sources may then
  60. | be assigned to any extra authentication guards you have defined.
  61. |
  62. | Supported: "database", "eloquent"
  63. |
  64. */
  65. 'providers' => [
  66. 'users' => [
  67. 'driver' => 'eloquent',
  68. 'model' => App\User::class,
  69. ],
  70. // 照葫芦画瓢, 添加'members'
  71. 'members' => [
  72. 'driver' => 'eloquent',
  73. 'model' => App\Member::class,
  74. ]
  75. // 'users' => [
  76. // 'driver' => 'database',
  77. // 'table' => 'users',
  78. // ],
  79. ],
  80. /*
  81. |--------------------------------------------------------------------------
  82. | Resetting Passwords
  83. |--------------------------------------------------------------------------
  84. |
  85. | You may specify multiple password reset configurations if you have more
  86. | than one user table or model in the application and you want to have
  87. | separate password reset settings based on the specific user types.
  88. |
  89. | The expire time is the number of minutes that the reset token should be
  90. | considered valid. This security feature keeps tokens short-lived so
  91. | they have less time to be guessed. You may change this as needed.
  92. |
  93. */
  94. 'passwords' => [
  95. 'users' => [
  96. 'provider' => 'users',
  97. 'table' => 'password_resets',
  98. 'expire' => 60,
  99. 'throttle' => 60,
  100. ],
  101. ],
  102. /*
  103. |--------------------------------------------------------------------------
  104. | Password Confirmation Timeout
  105. |--------------------------------------------------------------------------
  106. |
  107. | Here you may define the amount of seconds before a password confirmation
  108. | times out and the user is prompted to re-enter their password via the
  109. | confirmation screen. By default, the timeout lasts for three hours.
  110. |
  111. */
  112. 'password_timeout' => 10800,
  113. ];
  • 4- 前端登录验证中间件
  1. <?php
  2. namespace App\Http\Middleware;
  3. use App\Http\helper\CodeHelper;
  4. use Closure;
  5. use Illuminate\Support\Facades\Auth;
  6. use Illuminate\Support\Facades\DB;
  7. /* 前端验证登录中间件 */
  8. class AuthMember {
  9. public function handle($request, Closure $next, $guard = null) {
  10. // guard()方法是指定使用哪个"防守人"来验证; 不调用guard()方法, 则默认为'user';
  11. // guest()方法返回一个布尔值, 为真时, 表示当前没有用户登录
  12. if(Auth::guard('member')->guest()) { // 验证失败了, 用户没登录
  13. // 如果是ajax请求, 则返回相应
  14. if($request->ajax()) {
  15. $response = ['status' => 401, 'message' => '请先登录'];
  16. return response(json_encode($response), 200);
  17. }
  18. // 否则, 重定向到'member/login'路由
  19. return redirect()->guest('member/login');
  20. }
  21. return $next($request);
  22. }
  23. }
  • 注册中间件和路由略.
    1. 商品详情页(关注点: 立刻购买逻辑)
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <!-- 当前文档要用到阿里字体图标-->
  6. <link rel="stylesheet" href="/static/font/iconfont.css">
  7. <link rel="stylesheet" href="/static/css/shop/shop_detail.css">
  8. <title>商城详情页</title>
  9. <script src="/static/js/jquery3.4.1.js"></script>
  10. <link rel="stylesheet" href="/static/plugin/layui/css/layui.css">
  11. <script src="/static/plugin/layui/layui.js"></script>
  12. </head>
  13. <body>
  14. <!--公共页眉-->
  15. @include('/front/public/header')
  16. <!--主体全部放在main元素中-->
  17. <main>
  18. <!-- 商城公共头部-->
  19. <!--logo+搜索框+快捷入口区-->
  20. @include('/front/public/header_search')
  21. <!--为商品详情区块单独创建一个包含块,方便用网格布局-->
  22. <div class="detail">
  23. <!--商城详情页上部购买组件-->
  24. <div class="shop-detail-bug">
  25. <!--头部面包屑导航-->
  26. <nav>
  27. <a href="">首页&nbsp;&gt;&nbsp;</a>
  28. <a href="">图片写真&nbsp;&gt;&nbsp;</a>
  29. <a href="">日本&nbsp;&gt;&nbsp;</a>
  30. <a href="">颖宝宝</a>
  31. </nav>
  32. <article>
  33. @csrf
  34. <input type="hidden" name="id" value="{{$product['id']}}">
  35. <span><img src="{{$product['thumb']}}" alt=""></span>
  36. <div>
  37. <!--商品标题-->
  38. <h3>{{$product['title']}}</h3>
  39. <!--商品价格-->
  40. <div class="price">
  41. <span>本站特惠:</span>
  42. <span>&yen;{{$product['price']}}</span>
  43. </div>
  44. <!--基本描述-->
  45. <div class="desc">
  46. 销量: <span>13</span>&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;
  47. 累积评价: <span>3</span>&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;
  48. 好评率: <span>199%</span>
  49. </div>
  50. <!-- 购买数量-->
  51. <div class="buy-num">
  52. <label for="num">购买数量:</label><input type="number" id="num" value="1">
  53. </div>
  54. <!--购买按钮-->
  55. <div class="buy-btn">
  56. <button type="button" onclick="buy()">立即购买</button>
  57. <button><i class="iconfont icon-icon_tianjia"></i>加入购物车</button>
  58. </div>
  59. <!--售后承诺-->
  60. <div class="promise">
  61. <span><i class="iconfont icon-zhanghaoquanxianguanli"></i>本站保障</span>
  62. <span><i class="iconfont icon-icon_safety"></i>企业认证</span>
  63. <span><i class="iconfont icon-tianshenpi"></i>退款承诺</span>
  64. <span><i class="iconfont icon-kuaisubianpai"></i>免费换货</span>
  65. </div>
  66. </div>
  67. </article>
  68. </div>
  69. <!--商城详情页左下推荐商品列表-->
  70. <div class="shop-detail-recommend">
  71. <h3>推荐商品</h3>
  72. <div>
  73. <a href="">
  74. <img src="/static/images/shop/shop1.jpg" alt="">
  75. </a>
  76. <a href="">韩国美女最新海报促销美妆写真图集</a>
  77. <div class="hot">
  78. <span>热销:</span><span>8976</span>
  79. <span>价格:</span><span>&yen;99</span>
  80. </div>
  81. </div>
  82. <div>
  83. <a href="">
  84. <img src="/static/images/shop/shop2.jpg" alt="">
  85. </a>
  86. <a href="">韩国美女最新海报促销美妆写真图集</a>
  87. <div class="hot">
  88. <span>热销:</span><span>324</span>
  89. <span>价格:</span><span>&yen;798</span>
  90. </div>
  91. </div>
  92. <div>
  93. <a href="">
  94. <img src="/static/images/shop/shop3.jpg" alt="">
  95. </a>
  96. <a href="">韩国美女最新海报促销美妆写真图集</a>
  97. <div class="hot">
  98. <span>热销:</span><span>678</span>
  99. <span>价格:</span><span>&yen;630</span>
  100. </div>
  101. </div>
  102. <div>
  103. <a href="">
  104. <img src="/static/images/shop/shop4.jpg" alt="">
  105. </a>
  106. <a href="">韩国美女最新海报促销美妆写真图集</a>
  107. <div class="hot">
  108. <span>热销:</span><span>12</span>
  109. <span>价格:</span><span>&yen;980</span>
  110. </div>
  111. </div>
  112. </div>
  113. <!--商城详情页右下详情选项卡-->
  114. <div class="shop-detail-tab">
  115. <div class="tab">
  116. <span class="active">商品详情</span>
  117. <span>案例/演示</span>
  118. <span>常见问题</span>
  119. <span>累计评价</span>
  120. <span>产品咨询</span>
  121. </div>
  122. <div class="content">{!!$product['content']!!}</div>
  123. </div>
  124. <!--评论与回复-->
  125. <div class="public-comment-reply">
  126. <!-- 评论区-->
  127. <div class="comment">
  128. <h3>我要评论</h3>
  129. <div>
  130. <label for="comment"><img src="/static/images/user.png" alt=""></label>
  131. <textarea name="" id="comment"></textarea>
  132. </div>
  133. <button>发表评论</button>
  134. </div>
  135. <!-- 回复区-->
  136. <div class="reply">
  137. <h3>最新回复</h3>
  138. <div>
  139. <img src="/static/images/user.png" alt="">
  140. <div class="detail">
  141. <span>用户昵称</span>
  142. <span>留言内容: php中文网,是一个有温度,有思想的学习平台</span>
  143. <div>
  144. <span>2019-12-12 15:34:23发表</span>
  145. <span><i class="iconfont icon-dianzan"></i>回复</span>
  146. </div>
  147. </div>
  148. </div>
  149. <div>
  150. <img src="/static/images/user.png" alt="">
  151. <div class="detail">
  152. <span>用户昵称</span>
  153. <span>留言内容: php中文网,是一个有温度,有思想的学习平台</span>
  154. <div>
  155. <span>2019-12-12 15:34:23发表</span>
  156. <span><i class="iconfont icon-dianzan"></i>回复</span>
  157. </div>
  158. </div>
  159. </div>
  160. <div>
  161. <img src="/static/images/user.png" alt="">
  162. <div class="detail">
  163. <span>用户昵称</span>
  164. <span>留言内容: php中文网,是一个有温度,有思想的学习平台</span>
  165. <div>
  166. <span>2019-12-12 15:34:23发表</span>
  167. <span><i class="iconfont icon-dianzan"></i>回复</span>
  168. </div>
  169. </div>
  170. </div>
  171. <div>
  172. <img src="/static/images/user.png" alt="">
  173. <div class="detail">
  174. <span>用户昵称</span>
  175. <span>留言内容: php中文网,是一个有温度,有思想的学习平台</span>
  176. <div>
  177. <span>2019-12-12 15:34:23发表</span>
  178. <span><i class="iconfont icon-dianzan"></i>回复</span>
  179. </div>
  180. </div>
  181. </div>
  182. </div>
  183. </div>
  184. </div>
  185. </main>
  186. <!--公共页脚-->
  187. @include('/front/public/footer')
  188. </body>
  189. <script>
  190. layui.use(['layer'], function() {
  191. layer = layui.layer;
  192. });
  193. function buy() {
  194. $data = {};
  195. $data.id = parseInt($('input[name="id"]').val());
  196. $data._token = $('input[name="_token"]').val();
  197. $data.buy_count = $("#num").val();
  198. if(isNaN($data.id)) {
  199. return layer.alert('参数错误', {icon: 2});
  200. }
  201. // 下订单
  202. $.post(
  203. '/shop/create_order'
  204. , $data
  205. , function(res) {
  206. if(res.status != undefined && res.status == 401) {// 用户未登录
  207. layer.open({
  208. type: 2,
  209. title: '登录',
  210. shadowClose: false,
  211. shadow: 0.5,
  212. area: ['400px', '250px'],
  213. content: '/account/login'
  214. });
  215. } else if(res.status != undefined && res.status == '0') {// 下单成功
  216. layer.alert(res.message, {icon: 1});
  217. layer.open({
  218. type: 2,
  219. title: '扫码支持',
  220. shadowClose: false,
  221. shadow: 0.5,
  222. area: ['400px', '400px'],
  223. content: '/shop/pay'
  224. });
  225. } else if(res.status != undefined) {
  226. return layer.alert(res.message, {icon: 2});
  227. }
  228. }
  229. , 'json'
  230. );
  231. }
  232. </script>
  233. </html>
  • 6- 生成订单的控制器方法
  1. // 下订单
  2. public function createOrder(Request $req) {
  3. // 登录用户
  4. $member = Auth::guard('member')->user();
  5. // 商品id
  6. $pro_id = (int) $req->id;
  7. $product = DB::table('product')->where([['id', $pro_id], ['status', 1]])->getFirst();
  8. if(!$product) {
  9. return CodeHelper::failure_json('该商品不存在或未上架');
  10. }
  11. // 无货或供货不足
  12. if($product['stock'] < 0 || $product['stock'] < $req->buycount) {
  13. return CodeHelper::failure_json('库存不足');
  14. }
  15. // 使用时间戳+用户id+随机数1+随机数2生成订单号(这样拼接成的订单号出现重复号的概率很低了)
  16. $data['ord_no'] = time() . $member->id . rand(100, 500) . rand(501, 999);
  17. // 购买人id
  18. $data['member_id'] = (int) $member->id;
  19. // 商品id
  20. $data['pro_id'] = $pro_id;
  21. // 购买数量
  22. $data['count'] = $req->buy_count;
  23. // 金额
  24. $data['money'] = $req->buy_count * $product['price'];// 一定要从数据库里获取商品价格, 前端传过来的价格都不可信
  25. //
  26. $data['add_time'] = time();
  27. // 保存订单
  28. $oid = DB::table('orders')->insertGetId($data);
  29. // 减库存(用laravel提供的decrement()方法减库存)
  30. DB::table('product')->where('id', $pro_id)->decrement('stock', $req->buy_count);
  31. return CodeHelper::success_json('下单成功');
  32. }
Correcting teacher:GuanhuiGuanhui

Correction status:qualified

Teacher's comments:script标签尽量放在body标签的底部,另外适量加点效果图。
Statement of this Website
The copyright of this blog article belongs to the blogger. Please specify the address when reprinting! If there is any infringement or violation of the law, please contact admin@php.cn Report processing!
All comments Speak rationally on civilized internet, please comply with News Comment Service Agreement
0 comments
Author's latest blog post