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

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

岂几岂几
岂几岂几原创
2020年07月10日 00:40:111768浏览

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

学习心得

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

  • 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. }
声明:本文内容转载自脚本之家,由网友自发贡献,版权归原作者所有,如您发现涉嫌抄袭侵权,请联系admin@php.cn 核实处理。
全部评论
文明上网理性发言,请遵守新闻评论服务协议