iT邦幫忙

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

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

Node.js-Backend見聞錄(23):實作-商品系統(六)-修改訂單部分(二)

Node.js-Backend見聞錄(23):實作-商品系統(六)-修改訂單部分(二)

前言

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

修改訂單部分需求

  • 取得訂單資料(GET)
  • 修改訂單(PUT)
  • 刪除訂單(DELETE)
  • 新增單筆訂單資料(POST)

資料結構

新增的部分:

  • models
    • order
      • delete_model.js
      • order_one_product_model.js
.
├── 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
│   │    ├── delete_model.js
│   │    ├── one_order_model.js
│   │    ├── order_all_product_model.js
│   │    ├── order_one_product_model.js
│   │    └── update_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

開始實作

刪除訂單(DELETE)

筆者將這支API預設為client端需要輸入的資料為兩種情況,分別是:

  • 刪除單筆資料
    • headers
      • token: 會員經登入後所取得的token值
    • body
      • orderID: 1
      • productID: 1
  • 刪除多筆資料
    • headers
      • token: 會員經登入後所取得的token值
    • body
      • orderID: 1
      • productID: 1, 2

那要怎麼透過程式來區分到底會員再進行刪除動作時,其結果是「刪除單筆」還是「刪除多筆」呢?這部分我們可藉由資料重整的方式來處理。

資料重整

若要讓資料庫進行刪除訂單資料的動作是需要order_idmember_idproduct_id這三個欄位的值,才能準確的刪除我們所想要刪除的資料。等同於說我們預期資料應該要整理成像下述的樣子才能啟用SQL query

orderID = 1, memberID = 1, productID = 1

在JavaScript語系中,我們只要把資料整理成像下述那樣,就能處理這情況。

  • 單筆
[ { orderID: '1', memberID: 1, productID: '1' } ]
  • 多筆
[ { orderID: '1', memberID: 1, productID: '1' },
  { orderID: '1', memberID: 1, productID: '2' } ]

另外,我們依舊需要先判定欲刪除的這筆訂單是否存在及是否已經完成付款動作了。而這部分會繼續沿用在實作-商品系統(五)-修改訂單部分(一)中所寫的兩個判斷:

  • 判斷該筆訂單是否存在
  • 判斷該筆訂單是否已完成

判斷該筆訂單是否存在

const checkOrderData = function (orderID, memberID, productID) {
    return new Promise((resolve, reject) => {
        db.query('SELECT * FROM order_list WHERE order_id = ? AND member_id = ? AND product_id = ?', [orderID, memberID, productID], function (err, rows) {
            if (rows[0] === undefined) {
                resolve(false);
            } else {
                resolve(true);
            }
        })
    })
}

判斷該筆訂單是否已完成

const checkOrderComplete = function (orderID, memberID, productID) {
    return new Promise((resolve, reject) => {
        db.query('SELECT * FROM order_list WHERE order_id = ? AND member_id = ? AND product_id = ? AND is_complete = 0', [orderID, memberID, productID], function (err, rows) {
            if (rows[0] === undefined) {
                resolve(false);
            } else {
                resolve(true);
            }
        })
    })
}

將資料寫入資料庫進行刪除動作

models > order資料夾的delete_model.js檔案中寫入:

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

module.exports = function orderDelete(deleteList) {
    return new Promise(async (resolve, reject) => {
        let result = {};

        // 有幾筆資料就刪除幾次資料
        for (let key in deleteList) {
            let hasData = await checkOrderData(deleteList[key].orderID, deleteList[key].memberID, deleteList[key].productID);
            let hasComplete = await checkOrderComplete(deleteList[key].orderID, deleteList[key].memberID, deleteList[key].productID);
            if (hasData === false) {
                result.status = "刪除訂單資料失敗。"
                result.err = "找不到該筆資料。"
                reject(result);
            }
            else if (hasComplete === false) {
                result.status = "刪除訂單資料失敗。"
                result.err = "該筆資料已經完成。"
                reject(result);
            } else if (hasData === true && hasComplete === true) {
                db.query('DELETE FROM order_list WHERE order_id = ? and member_id = ? and product_id = ?', [deleteList[key].orderID, deleteList[key].memberID, deleteList[key].productID], function (err, rows) {
                    if (err) {
                        console.log(err);
                        result.err = "伺服器錯誤,請稍後在試!"
                        reject(result);
                        return;
                    }
                    result.status = "刪除訂單資料成功。";
                    result.deleteList = deleteList;
                    resolve(result);
                });
            }
        }
    })
}

之後,將model匯入controllers > order資料夾的modify_controller.js中並寫入個deleteOrderProduct的function。

deleteOrderProduct(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 orderID = req.body.orderID;
                const memberID = tokenResult;
                
                // 防呆處理,去處空白
                const productID = req.body.productID.replace(" ", "");
                const splitProductID = productID.split(",");

                let deleteList = [];

                // 重整成資料庫可辨識的順序
                for (let i = 0; i < splitProductID.length; i += 1) {
                    deleteList.push({orderID: orderID, memberID: memberID, productID: splitProductID[i]});
                }
                deleteOrderData(deleteList).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.get('/order/member', orderGetMethod.getOneOrder);

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

// 更改單筆訂單資料
router.put('/order', orderModifyMethod.updateOrderProduct);

// 刪除訂單資料
router.delete('/order', orderModifyMethod.deleteOrderProduct);

module.exports = router;

測試

假設目前order_list的table中有五筆資料:

我們用訂單2的資料來測試多筆刪除,訂單3則用來測試單筆刪除。

  • 多筆刪除
    • HTTP method: DELETE
    • HTTP url : localhost:3000/order
    • Body中選擇x-www-form-urlencoded
    • headers
      • Content-Type: application/x-www-form-urlencoded
      • token: 會員經登入後所取得的token值
    • body
      • orderID: 2
      • productID: 1,2

其結果:

MySQL那邊的結果:

  • 單筆刪除
    • HTTP method: DELETE
    • HTTP url : localhost:3000/order
    • Body中選擇x-www-form-urlencoded
    • headers
      • Content-Type: application/x-www-form-urlencoded
      • token: 會員經登入後所取得的token值
    • body
      • orderID: 3
      • productID: 3

其結果:

MySQL那邊的結果:

新增單筆訂單資料(POST)

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

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

也就是需要輸入特定訂單的編號,要額外新增的商品編號及其數量。但在實作前我們必須要做兩件事情,分別是:

  1. 確認是否有該筆訂單資料。
  2. 確認該筆訂單是否已經完成。
  3. product的table撈取特定商品的價格。

先至models> order資料夾的order_one_product_model.js檔案來進行撰寫:

確認是否有該筆訂單資料

這部分我們一樣拿先前確認訂單資料的function來做判斷:

const checkOrderData = (orderID, memberID, productID) => {
    return new Promise((resolve, reject) => {
        db.query('SELECT * FROM order_list WHERE order_id = ? AND member_id = ? AND product_id = ?', [orderID, memberID, productID], function (err, rows) {
            if (rows[0] === undefined) {
                resolve(false);
            } else {
                resolve(true);
            }
        })
    })
}

但這部分的做法有別於之前都是用來確認是有該訂單的,而是用來確認是沒有該筆訂單才能進行新增單筆訂單資料的動作。

確認該筆訂單是否已經完成

const checkOrderComplete = (orderID) => {
    return new Promise((resolve, reject) => {
        db.query('SELECT is_complete FROM order_list WHERE order_id = ?', orderID, function (err, rows) {
            if (rows[0].is_complete === 1) {
                resolve(false);
            } else {
                resolve(true);
            }
        })
    })
}

提取商品價格

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);
        })
    })
}

將單筆訂單資料寫入資料庫

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

module.exports = function postOneOrderData(orderOneList) {
    let result = {};
    return new Promise(async (resolve, reject) => {

        const hasData = await checkOrderData(orderOneList.orderID, orderOneList.memberID, orderOneList.productID);

        const hasComplete = await checkOrderComplete(orderOneList.orderID);

        if (hasData === true) {
            result.status = "新增單筆訂單資料失敗。"
            result.err = "已有該筆訂單資料!"
            reject(result)
        } else if (hasComplete === false){
            result.status = "新增單筆訂單資料失敗。"
            result.err = "該筆訂單已經完成。"
            reject(result)
        } else if (hasData === false) {

            const price = await getProductPrice(orderOneList.productID);

            const orderList = {
                order_id: orderOneList.orderID,
                member_id: orderOneList.memberID,
                product_id: orderOneList.productID,
                order_quantity: orderOneList.quantity,
                order_price: orderOneList.quantity * price,
                is_complete: 0,
                order_date: orderOneList.createDate
            }

            db.query('INSERT INTO order_list SET ?', orderList, function (err, rows) {
                // 若資料庫部分出現問題,則回傳「伺服器錯誤,請稍後再試!」的結果。
                if (err) {
                    console.log(err);
                    result.status = "新增單筆訂單資料失敗。"
                    result.err = "伺服器錯誤,請稍後在試!"
                    reject(result);
                    return;
                }
                result.status = "新增單筆訂單資料成功。"
                result.orderList = orderList
                resolve(result);
            })
        }
    })
}

再來將model的部分匯入controllers > order資料夾的modify_controller.js檔案,並放置在postOrderOneProduct的function中:

postOrderOneProduct(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 orderOneList = {
                    orderID: req.body.orderID,
                    memberID: memberID,
                    productID: req.body.productID,
                    quantity: req.body.quantity,
                    createDate: onTime()
                }
                orderOneProductData(orderOneList).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.get('/order/member', orderGetMethod.getOneOrder);

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

// 更改單筆訂單資料
router.put('/order', orderModifyMethod.updateOrderProduct);

// 刪除訂單資料
router.delete('/order', orderModifyMethod.deleteOrderProduct);

// 訂單筆訂單
router.post('/order/addoneproduct', orderModifyMethod.postOrderOneProduct);

module.exports = router;

測試

這部分我們使用Postman來進行測試:

  • HTTP method: POST

  • HTTP url : localhost:3000/order/addoneproduct

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

  • headers

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

    • orderID: 1
    • productID: 3
    • quantity: 2

測試結果為:

MySQL的狀況為:

小結

在這階段我們已經完成修改訂單部分的四支API,而下個章節將接續開發確認訂單部分的功能。


上一篇
Node.js-Backend見聞錄(22):實作-商品系統(五)-修改訂單部分(一)
下一篇
Node.js-Backend見聞錄(24):實作-商品系統(七)-確認訂單部分
系列文
Node JS-Back end見聞錄31

尚未有邦友留言

立即登入留言