iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 22
0
Modern Web

Node JS-Back end見聞錄系列 第 22

Node.js-Backend見聞錄(21):實作-商品系統(四)-訂單列表及訂購商品部分(二)

Node.js-Backend見聞錄(21):實作-商品系統(四)-訂單列表及訂購商品部分(二)

前言

接續實作-商品系統(三)-訂單列表及訂購商品部分(一),我們將完成訂單列表及訂購商品部分的開發。

訂單列表及訂購商品需求

  • 訂單列表(GET)
  • 取得商品資料(GET)
  • 進行訂購(POST)

資料結構

新增的部分:

  • controllers
    • order
      • get_controller.js
      • modify_controller.js
  • models
    • order
      • all_order_model.js
      • order_all_product_model.js
  • routes
    • order
.
├── app.js
├── bin
│   └── www
├── config
│   └── development_config.js
├── controllers
│   ├── member
│   │    └── modify_controller.js
│   ├── order
│   │    ├── get_controller.js
│   │    └── modify_controller.js
│   ├── product
│   │    └── get_controller.js
├── models
│   ├── member
│   │    ├── encryption_model.js
│   │    ├── login_model.js
│   │    ├── register_model.js
│   │    ├── update_model.js
│   │    └── verifyication_model.js
│   ├── order
│   │    ├── all_order_model.js
│   │    └── order_all_product_model.js
│   ├── product
│   │    └── all_product.js
│   └── connection_db.js
├── package.json
├── public
│   ├── images
│   ├── javascripts
│   └── stylesheets
│       └── style.css
├── routes
│   ├── member.js
│   ├── order.js
│   └── product.js
├── sevice
│   └── member_check.js
├── views
    ├── error.ejs
    └── index.ejs
├── .env
└── .gitignore

開始實作

接續我們剩下訂單列表(GET)進行訂購(POST)的部分。我們先來實作進行訂購(POST)的功能,因為若先做完訂單列表(GET)功能,我們並沒有資料可以來測試這部分(笑)。

進行訂購(POST)

筆者將這支API預設為client端需要輸入的資料為:

  • headers
    • token: 會員經登入後所取得的token值
  • body
    • productID: 1,2
    • quantity: 3,4

在這當中,較為特殊的是productIDquantity之間的關係,而它們之間的關係等同於假設有一筆訂單,會員買了商品編號1及商品編號2的東西,且各自的數量依序為3個及4個。這時,前端開發的夥伴只要將資料整理成上述情況,當我們server端接收到後,將資料切割成一個object的形式:

obejct {
    1: 3,
    2: 4
}

接續使用for迴圈的方式,依序將訂單資料存入資料庫中。這種設計方式是為了因應在實作-商品系統(二)-資料庫設計所提到的order_list部分的資料欄位設計:

order_id customer_id product_id order_quantity order_price is_complete create_date update_date
1 1 1 2 24.00 0 2018-01-05 datetime
1 1 2 1 5.00 0 2018-01-05 datetime
1 1 3 1 20.00 0 2018-01-05 datetime

等同於假設會員1買了三樣物品,在範例中分別是商品編號1、商品編號2及商品編號3的東西。且這三樣物品都算是同一筆訂單的設計方式。

為了做到這個方式,首先我們先處理models > order資料夾的order_all_product_model.js檔案:

但在撰寫處理這部分時,我們會先碰到兩個問題:

  1. order_id當初在建立資料庫時不是使用AUTO_INCREMENT,也就是不會自行新增order_id的編號。(當然,這部分是故意的,因為假如我們使用了自動新增編號的功能,代表我們沒辦法達到上述的table設計。)

  2. 商品沒有夾帶自身價格,等同於我們還要額外去product的table去撈商品價格,這樣才能計算order_price的價格。

自動遞增order_id

這部分我們可以透過資料庫的MAX()方式來幫我們找出最大值:

// 取得訂單id
const getOrderID = () => {
  return new Promise((resolve, reject) => {
    db.query('SELECT MAX(order_id) AS id FROM order_list', function (err, rows, fields) {
      if (err) {
        console.log(err);
        reject(err);
        return;
      }
      resolve(rows[0].id);
    })
  })
}

待取得後,等等在主要的function中,我們接續在幫該值+1就能取得新的order_id值了。

取得個別商品價格

這部分因為我們在要求client端將資料傳過來時的productID剛好就是producttable本身各個商品的id。所以這時只要將該值帶進入資料庫進行SELECT動作就能在取到個別商品的價格:

// 取得商品價格
const getProductPrice = (productID) => {
  return new Promise((resolve, reject) => {
    db.query('SELECT price FROM product WHERE id = ?', productID, function (err, rows) {
      if (err) {
        console.log(err);
        reject(err);
        return;
      }
      resolve(rows[0].price);
    })
  })
}

切分資料,並INSERT資料到order_list的table中

這部分就將我們剛剛額外撰寫的兩個funciton放置其中,並處理一開始所提到將client端輸入的資料進行切分,轉變成object的型態,並將資料帶入到table中:

const db = require('../connection_db');

module.exports = function orderProductListData(orderList) {
  //訂購整筆商品
  let result = {};

  return new Promise(async (resolve, reject) => {
    // 提取orderID
    let orderID = await getOrderID() + 1;

    const products = orderList.productID;
    const productArray = products.split(',');
    // console.log("productArray: " + productArray);
    const quantitys = orderList.quantity;
    const quantityArray = quantitys.split(',');
    // console.log("quantityArray: " + quantityArray);

    //productID與quantity合併成新object
    //array1 [3, 2, 1]
    //array2 [1, 2, 3]
    //merge為object:{
    //  3: 1,
    //  2: 2,
    //  1, 3
    //}

    let productQuantity = {};
    for (let i in productArray) {
      // console.log('productArray i: ' + productArray[i]);
      let index = productArray.indexOf(productArray[i]);
      // console.log('the index: ' + index);
      for (let j in quantityArray) {
        // console.log('quantityArray j: ' + quantityArray[j]);
        productQuantity[productArray[i]] = quantityArray[index];
        // console.log('new quantityArray j: ' + quantityArray[index]);
      }
    }

    let orderAllData = [];
    for (let key in productQuantity) {
      // console.log(orderID);
      const price = await (getProductPrice(key));
      const orderData = {
        order_id: orderID,
        member_id: orderList.memberID,
        product_id: key,
        order_quantity: parseInt(productQuantity[key]),
        order_price: parseInt(price) * parseInt(productQuantity[key]),
        order_date: orderList.orderDate,
        is_complete: 0
      };

      // insert order data.
      db.query('INSERT INTO order_list SET ?', orderData, function (err, rows) {
        if (err) {
          console.log(err);
          result.err = "伺服器錯誤,請稍後在試!"
          reject(result);
          return;
        }
      })
      orderAllData.push(orderData);
    }

    result.state = "訂單建立成功。";
    result.orderData = orderAllData
    resolve(result);
  })
}

由於model部分的設置已經結束,接續我們到controllers > order資料夾的modify_controller.js來寫入判斷是否為會員及匯入剛剛所撰寫的model部分的程式:

const Check = require('../../service/member_check');

const verify = require('../../models/member/verification_model');
const orderProductListData = require('../../models/order/order_all_product_model');

check = new Check();

module.exports = class ModifyOrder {
    // 訂整筆訂單
    postOrderAllProduct(req, res, next) {
        const token = req.headers['token'];
        //確定token是否有輸入
        if (check.checkNull(token) === true) {
            res.json({
                err: "請輸入token!"
            })
        } else if (check.checkNull(token) === false) {
            verify(token).then(tokenResult => {
                if (tokenResult === false) {
                    res.json({
                        result: {
                            status: "token錯誤。",
                            err: "請重新登入。"
                        }
                    })
                } else {
                    const memberID = tokenResult;
                    const orderList = {
                        memberID: memberID,
                        productID: req.body.productID,
                        quantity: req.body.quantity,
                        orderDate: onTime(),
                    }
                    orderProductListData(orderList).then(result => {
                        res.json({
                            result: result
                        })
                    }, (err) => {
                        res.json({
                            result: err
                        })
                    })
                }
            })
        }
    }
}

//取得現在時間,並將格式轉成YYYY-MM-DD HH:MM:SS
const onTime = () => {
    const date = new Date();
    const mm = date.getMonth() + 1;
    const dd = date.getDate();
    const hh = date.getHours();
    const mi = date.getMinutes();
    const ss = date.getSeconds();

    return [date.getFullYear(), "-" +
        (mm > 9 ? '' : '0') + mm, "-" +
        (dd > 9 ? '' : '0') + dd, " " +
        (hh > 9 ? '' : '0') + hh, ":" +
        (mi > 9 ? '' : '0') + mi, ":" +
        (ss > 9 ? '' : '0') + ss
    ].join('');
}

再來,設置個API URL給這部分的功能。至routes資料夾的order.js檔案中:

var express = require('express');
var router = express.Router();

const OrderModifyMethod = require('../controllers/order/modify_controller');

orderModifyMethod = new OrderModifyMethod();

// 訂整筆訂單
router.post('/order', orderModifyMethod.postOrderAllProduct);

module.exports = router;

最後,別忘了app.js檔案也要寫入:

const order = require('./routes/order.js');

app.use('/', order);

測試

這部分我們透過Postman來進行測試:

  • HTTP method: POST

  • HTTP url : localhost:3000/order

  • Body中選擇x-www-form-urlencoded

  • headers

    • Content-Type: application/x-www-form-urlencoded
    • token: 會員經登入後所取得的token值
  • body

    • productID: 1,2
    • quantity: 3,4

其結果:

訂單列表(GET)

這部分所需要做的事情就單純許多,只要去order_list的table中,將整個訂單資料撈出來就可以了。

先到models > order資料夾的all_order_model.js,並輸入:

const db = require('../connection_db');

module.exports = function getAllOrderData() {
    let result = {};
    return new Promise((resolve, reject) => {
        db.query('SELECT * FROM order_list', function (err, rows) {
            // 若資料庫部分出現問題,則回傳「伺服器錯誤,請稍後再試!」的結果。
            if (err) {
                console.log(err);
                result.status = "取得全部訂單資料失敗。"
                result.err = "伺服器錯誤,請稍後在試!"
                reject(result);
                return;
            }
            // 若資料庫部分沒問題,則回傳全部訂單資料。
            resolve(rows);
        })
    })
}

之後到controllers > order資料夾的get_controller.js檔案,來把model及辨識會員部分匯入進去:

const Check = require('../../service/member_check');

const verify = require('../../models/member/verification_model');
const orderData = require('../../models/order/all_order_model');

check = new Check();

module.exports = class GetOrder {
    // 取得全部訂單資料
    getAllOrder(req, res, next) {
        const token = req.headers['token'];
        //確定token是否有輸入
        if (check.checkNull(token) === true) {
            res.json({
                err: "請輸入token!"
            })
        } else if (check.checkNull(token) === false) {
            verify(token).then(tokenResult => {
                if (tokenResult === false) {
                    res.json({
                        result: {
                            status: "token錯誤。",
                            err: "請重新登入。"
                        }
                    })
                } else {
                    orderData().then(result => {
                        res.json({
                            result: result
                        })
                    }, (err) => {
                        res.json({
                            result: err
                        })
                    })
                }
            })
        }
    }
}

最後,再給該功能一個API URL。到routes資料夾的order.js檔案中寫入:

var express = require('express');
var router = express.Router();

const OrderGetMethod = require('../controllers/order/get_controller');
const OrderModifyMethod = require('../controllers/order/modify_controller');

orderGetMethod = new OrderGetMethod();
orderModifyMethod = new OrderModifyMethod();

// 取得全部訂單資料
router.get('/order', orderGetMethod.getAllOrder);

// 訂整筆訂單
router.post('/order', orderModifyMethod.postOrderAllProduct);

module.exports = router;

測試

依舊使用Postman來進行測試:

  • HTTP method: GET

  • HTTP url : localhost:3000/order

  • headers

    • token: 會員經登入後所取得的token值

其結果:

小結

在這階段我們已經完成訂單列表及訂購商品部分的三支API,而下個章節將接續開發修改訂單部分的功能。


上一篇
Node.js-Backend見聞錄(20):實作-商品系統(三)-訂單列表及訂購商品部分(一)
下一篇
Node.js-Backend見聞錄(22):實作-商品系統(五)-修改訂單部分(一)
系列文
Node JS-Back end見聞錄31

尚未有邦友留言

立即登入留言