middle

NODE中间层-day6html

01-反馈前端

姓名 意见或建议
*** 今天最后一天了,刚哥讲完临走前来首歌吧,十分不舍的你。
*** 老师能不能介绍一下koa
*** 周哥今天是你最后一天了,,,难受ing 我还想听你讲一遍组件之间的传智,尤为是子传父和非父子 ..谢谢您咧❀❀❀❀node

02-回顾express

...json

  • 头部购物车信息展现:
    • 购物车中全部商品件数总和
    • 购物车中全部商品名称的列表
  • 在全局中间件去实现获取购物车数据
    • global的中间件
    • 已有获取分类信息异步操做
    • 也有获取购物车数据异步操做
    • 把分类业务 和 购物车业务 封装成两个promise对象
    • Promise.all()

中间件内代码:api

// 分类业务
const setCategory = () => {
  return new Promise((resolve, reject) => {
    if (req.app.locals.category) {
      res.locals.category = req.app.locals.category
      // 处理成功
      resolve()
    } else {
      categoryModel.getCategory().then(data => {
        // 缓存
        req.app.locals.category = data
        res.locals.category = data
        resolve()
      }).catch(err => reject(err))
    }
  })
}

// setCategory().then(()=>{
//   next()
// }).catch(err=>next(err))

// 购物车业务
const setHeadCart = () => {
  return new Promise((resolve, reject) => {
    if (!req.session.user) {
      const cookieString = req.cookies[cart.key] || '[]'
      // [{id:100,amount:3},...]
      const cartList = JSON.parse(cookieString)
      const promiseArr = cartList.map(item => productModel.getProductBase(item.id))
      Promise.all(promiseArr).then(results => {
        // [{id:'',name:'',...},...]
        // 总件数
        // Array.reduce()  遍历---累加
        // arr.reduce((prev,item)=>prev+item,0)
        // item 遍历的时候每一项的值
        // prev 上一次回调函数的返回结果
        // 若是是第一次遍历 就没有上一次 最好设置一个默认值
        // reduce(callback,initValue)  initValue 起始值
        const count = cartList.reduce((prev,item) => item.amount + prev, 0)
        // 名称数组 ['名字1','名字2']
        const nameList = results.map(item => item.name)
        res.locals.headCart = {count, nameList}
        resolve()
      }).catch(err => {
        reject(err)
      })
    } else {
      cartModel.getCart(req.session.user.id).then(data => {
        res.locals.headCart = {
          count: data.reduce((prev,item) => item.amount + prev, 0),
          nameList: data.map(item => item.name)
        }
        resolve()
      }).catch(err => {
        reject(err)
      })
    }
  })
}

Promise.all([
  setCategory(),
  setHeadCart()
]).then(results => {
  // 两个操做成功
  // 在购物车页面  修改数量的时候  同时修改头部购物车的数量
  next()
}).catch(err => {
  next(err)
})
复制代码

头部代码:数组

<div class="yui3-u Right shopArea">
  <div class="fr shopcar">
    <div class="show-shopcar">
      <span class="car"></span>
      <a class="sui-btn btn-default btn-xlarge" href="/cart">
        <span>个人购物车</span>
        <i class="shopnum">{{headCart.count}}</i>
      </a>
      <div class="clearfix shopcarlist">
        <ul>
          {{each headCart.nameList item i}}
          <li>{{item}}</li>
          {{/each}}
        </ul>
      </div>
    </div>
  </div>
</div>
复制代码

// find() findIndex() includes() from() [].foreach.call( 伪数组,callback) map filter reducepromise

补充购物车数量联动头部购物车数量:缓存

// 伪数组
const $arr = $('.cart-list [type="text"]')
const headCount = [].reduce.apply($arr,[(prev,item)=>+item.value+prev,0])
$('.shopnum').html(headCount)
复制代码

03-结算-业务分析服务器

  • 发起结算业务:购物车选中了一些商品后 发起业务
  • 获取选中的商品ID (100,101)字符串 提交给后台
  • 结算:
    • 生成订单

    • 核对订单

      // 6. 提交结算数据 ('.sum-btn').on('click', function () {
  constarr = ('.cart-list [type="checkbox"]:checked')
  const ids = []arr.each(function (i,item) { ids.push(item.dataset.id) }) if (!ids.length) return alert('请选择商品后去结算') location.href = '/order/add?ids=' + ids.join(',') })

04-结算-路由规则

  • router.get('/order/add',checkLogin, userController.orderAdd)
    router.get('/checkout',checkLogin, userController.checkout)
    复制代码
  • 生成订单后:作什么???
  • 重定向:结算页面

05-结算-生成订单

// 生成订单
exports.orderAdd = (req, res, next) => {
  const items = req.query.ids
  const userId = req.session.user.id
  // 数据操做
  userModel.createOrder(userId, items)
    .then(data => {
      // data  订单数据
      // 未来结算页面须要根据订单编号查询订单信息
      res.redirect('/checkout?num=' + data.order_number)
    })
    .catch(err => next(err))
}
// 生成订单
exports.checkout = (req, res, next) => {
  // 渲染页面:
  // a. 订单数据
  // b. 收货地址列表数据
  res.send('结算页面')
}
复制代码

06-结算-页面渲染

  • 获取数据:订单数据,收货地址数据

    const num = req.query.num const userId = req.session.user.id // 渲染页面: // a. 订单数据 // b. 收货地址列表数据 Promise.all([ userModel.getOrder(num), userModel.getAddressList(userId) ]).then(results=>{ res.send(results) }).catch(err=>next(err))

  • 渲染页面

    • 加了一块内容:默认选中的收货地址--->后台生成默认把你的收货地址中的第一条看成默认地址

07-结算-新增收货地址

  • 路由: /order/address post

    // 让表单外的按钮去控制表单 form ----> id="addressForm"
    button -----> form="addressForm" // 去掉 属性 阻止提交 data-ok="modal"

    exports.orderAddressAdd = (req, res, next) => { // 添加收货地址,跳转当前的订单结算页面 // 须要数据:收件人 地址 手机号 邮编 订单编号 const {name, address, phone, code, num} = req.body userModel.addAddress(req.session.user.id, name, address, phone, code) .then(data => { res.redirect('/checkout?num=' + num) }).catch(err => next(err)) }

08-结算-选择收货地址

  • 路由: /order/address get
  • 分析:
    • 选中的收货地址数据:从order取出,属于订单
    • 其余的数据,从收货地址列表中取出,和订单无关,列表这里选中
  • 结果:修改order
  • 修改:order中的express_address字段
    • 准备字段的值:根据收货地址数据的ID去查询

    • 传order的num

      router.get('/order/address',checkLogin, userController.orderAddressEdit) router.get('/checkout',checkLogin, userController.checkout)

      exports.orderAddressEdit = (req, res, next) => { // num 订单编号 addressId 选中的收货地址数据ID const num = req.query.num const addressId = req.query.addressId const userId = req.session.user.id // 调用接口 先调用获取收货地址的接口 在去修改订单 userModel.getAddress(userId, addressId) .then(data => { // data 单条收货地址信息数据 {id,name,address,phone,code} const address = ${data.name} ${data.address} ${data.phone} ${data.code} return userModel.editOrderAddress(num, address) }) .then(data => { // 回到结算页面 重定向 res.redirect(/checkout?num=${num}&addressId=${addressId}) }).catch(err => next(err)) }

      // 修改收货地址的时候才会传 可能没有这项数据 const addressId = req.query.addressId || ''

      {{if !addressId}}

    • {{else}}
    • {{/if}}

09-支付-业务分析

  • 买家帐号 jfjbwb4477@sandbox.com
  • 登陆密码 111111
  • 支付密码 111111

10-支付-了解alipay沙箱

  • 测试环境

11-支付-生成支付地址

依赖 node-alipay-sdk

var ali = new Alipay({
	// 平台id
    appId: '2016080300159077',
    // 通知支付结果的地址
    notifyUrl: 'http://www.xxx.com/callback/alipay',
    // 私钥
    rsaPrivate: fs.readFileSync(path.resolve('./pem/sandbox_private.pem'), 'utf-8'),
   	// 公钥
    rsaPublic: fs.readFileSync(path.resolve('./pem/sandbox_ali_public.pem'), 'utf-8'),
    // 沙箱
    sandbox: true,
    // 加密类型
    signType: 'RSA2',
    // 日志
    openLog: true
});

var params = ali.pagePay({
    // 品优购商品
    subject: '测试商品',
    // 那些商品
    body: '测试商品描述',
    // 平台交易编号
    outTradeId: outTradeId,
    // 超时时间
    timeout: '10m',
    // 支付的金额
    amount: '10.00',
    // 产品类型  0 虚拟 1 实物
    goodsType: '0',
    // 二维码类型
    qrPayMode: 0
});
复制代码

params:加密后的传参

支付宝网关:openapi.alipaydev.com/gateway.do

最终支付的地址= 支付宝网关+ params

工具:uitl/alipay.js

12-支付-支付页面付款

<a class="sui-btn btn-danger btn-xlarge fr" href="/pay?num={{order.order_number}}">当即支付</a>

// 支付跳转
exports.pay = (req, res, next) => {
  const num = req.query.num
  userModel.getOrder(num)
    .then(data => {
      const payUrl = getPayUrl(data)
      res.redirect(payUrl)
    })
    .catch(err => next(err))
}
复制代码

13-支付-付款成功后回跳

  • 回调 平台系统 品优购
  • 回调 地址: http://127.0.0.1:3000/pay/callback
  • // 回调的地址 alipay.js
    return_url: 'http://127.0.0.1:3000/pay/callback'
    复制代码

14-支付-回跳后修改订单

// 支付成功后的回调
exports.callback = (req, res, next) => {
  const pay_status = 1 //支付成功
  const trade_no = req.query.trade_no  //支付交易流水
  const send_status = 0
  const num = req.query.out_trade_no  // 订单编号
  userModel.editOrder(num, pay_status, send_status, trade_no)
    .then(data=>{
      // 支付成功的提示页面  包含订单信息
      res.locals.order = data
      res.render('callback.art')
    }).catch(err=>next(err))
}
复制代码

15-支付-了解notify功能

  • 才是去修改订单状态的操做
  • 操做;支付宝服务器 请求 平台服务器
  • 是外网环境 访问不到 局域网
  • 通知 本地服务器 接收不到
  • 没法完成 修改订单的 操做
  • 因此:把修改订单的操做放到 callback 去执行 http://127.0.0.1:3000/pay/callback

16-扩展-自动登陆

  • 访问该网站的时候 实现自动登陆

  • cookie存储 {id:'',pw:''}

  • 前提:当前没有登陆 存储了自动登陆的信息

    exports.autoLogin = (req, res, next) => { const cookieString = req.cookies[autoLoginConfig.key] if (!req.session.user && cookieString) { // 自动登陆 const autoUser = JSON.parse(cookieString) //{id,pw} userModel.getUser(autoUser.id).then(data => { if (data.password === autoUser.pw) { // 自动登陆成功 req.session.user = data next() } else { // 匹配不正确 无效信息 清除 res.cookie(autoLoginConfig.key,'') } }) } else { next() } }

17-总结

  • node中间层
    • node调用接口
    • 后台渲染
    • 返回json前端渲染
  • UI和接口服务器解耦
  • 技术栈统一
  • 并发高

目标:

  • 增强nodejs开发能力
  • 了解 电商业务
  • 简历:加一笔
相关文章
相关标签/搜索