Create order Schema
// models\order.js const mongoose = require('mongoose') const Product = require('./product') const Category = require('./category') // Create snapshot type // Clone product Schema const snapshotProductSchema = Product.schema.clone() // Clone classification Schema const snapshopCategorySchema = Category.schema.clone() // Delete the unique setting of category name, otherwise save the same products [n] snapshot. category. Name will fail snapshopCategorySchema.path('name', String) // Change the type property from ObjectId to category Schema is used to store type snapshots snapshotProductSchema.path('category', snapshopCategorySchema) const orderSchema = new mongoose.Schema( { // Product information products: [ { // Product association id product: { type: mongoose.Schema.Types.ObjectId, ref: 'Product' }, // Product snapshot information (avoid being unable to view product information from the order after the product is deleted) snapshot: snapshotProductSchema, // Product purchase quantity count: Number } ], // Merchant order ID (alipay asynchronous notification return) out_trade_no: String, // alipay transaction ID (alipay generated ID returned by alipay asynchronous notification) trade_no: String, // Total order amount amount: Number, // Receiving address address: String, // Order status // Unpaid = > unpaid // Paid = > paid // Shipped = > in transit // Completed = > completed // Cancelled = > cancelled status: { type: String, default: 'Unpaid', // enum uses exceptions and uses validate instead validate: { validator(v) { return ['Unpaid', 'Paid', 'Shipped', 'Delivered', 'Cancelled'].includes(v) }, message: '{VALUE} Is not a valid order status' } }, // Order user user: { type: mongoose.Schema.Types.ObjectId, ref: 'User' } }, { timestamps: true } ) module.exports = mongoose.model('Order', orderSchema)
api - order management - create order
Create order
Define an api to create an order, which is used to skip the payment direct test, and then convert it into a method, which is only called in the api/verifySignature interface.
// controllers\order.js const Order = require('../models/order') const Product = require('../models/product') // Create order const create = async (req, res) => { // order => { // trade_no, // out_trade_no, // amount, // address, // products, // user, // result // } const order = req.body // Add order status order.status = order.result ? 'Paid' : 'Unpaid' // Delete signature verification result delete order.result // Get product snapshot information const snapshotObject = {} await Product.find({ _id: { $in: order.products.map(({ product }) => product) } }) .populate({ path: 'category', select: '_id name' }) .exec() .then(products => { products.forEach(product => { snapshotObject[product._id] = product }) }) // Populate product snapshot information order.products.forEach(item => { item.snapshot = snapshotObject[item.product] }) // Save order // MyModel.create() and new mymodel() Same as save() // The difference is mymodel Create () can save multiple document s const data = await Order.create(order) // Change product inventory and sales res.json({ message: 'Order created successfully' }) } module.exports = { create }
// routes\order.js const express = require('express') const { create } = require('../controllers/order') const router = express.Router() // Create order (skip payment) router.post('/order/create', create) module.exports = router
Change product inventory and sales
// controllers\product.js const Order = require('../models/order') ... // Update the inventory and sales volume of products under the order const updateProductQuantityAndSold = orderId => { Order.findById(orderId, (error, order) => { if (error || !order) { console.error('Failed to obtain order information when updating production inventory and sales volume', error) return } // Generate updateOne command const bulkOps = order.products.map(item => ({ updateOne: { filter: { _id: item.product }, update: { // $inc increases the specified value by the specified amount $inc: { quantity: -item.count, sold: +item.count } } } })) // Execute multiple commands at once Product.bulkWrite(bulkOps, {}, error => { if (error) { console.error('Product inventory and sales volume update failed', error) } }) }) } module.exports = { create, getProductById, read, update, remove, list, listRelated, listCategories, photo, updateProductQuantityAndSold }
// controllers\order.js const { updateProductQuantityAndSold } = require('./product') // Change product inventory and sales updateProductQuantityAndSold(data._id)
Convert the api into a method and call
// controllers\order.js const Order = require('../models/order') const Product = require('../models/product') const { updateProductQuantityAndSold } = require('./product') // Create order (to skip payment test) const create = async (req, res) => { const order = req.body try { await createOrder(order) res.json({ message: 'Order created successfully' }) } catch (e) { res.status(400).json({ message: 'Order creation failed', errors: e }) } } // Create order (used to be called after successful payment) const createOrder = async order => { // order => { // trade_no, // out_trade_no, // amount, // address, // products, // user, // result // } // Add order status order.status = order.result ? 'Paid' : 'Unpaid' // Delete signature verification result delete order.result // Get product snapshot information const snapshotObject = {} await Product.find({ _id: { $in: order.products.map(({ product }) => product) } }) .populate({ path: 'category', select: '_id name' }) .exec() .then(products => { products.forEach(product => { snapshotObject[product._id] = product }) }) // Populate product snapshot information order.products.forEach(item => { item.snapshot = snapshotObject[item.product] }) // Save order // MyModel.create() and new mymodel() Same as save() // The difference is mymodel Create () can save multiple document s const data = await Order.create(order) // Change product inventory and sales updateProductQuantityAndSold(data._id) } module.exports = { create, createOrder }
Create order after asynchronous notification
// controllers\alipay.js ... const { createOrder } = require('../controllers/order') ... // Payment successful callback const alipayNotifyUrl = async (req, res) => { // 1. Signature verification // Create SDK instance const alipaySdk = new AlipaySdk(alipaySdkConfig) try { // result is a Boolean value, indicating whether the signature is passed const result = await alipaySdk.checkNotifySign(req.body) // Get return parameters const passback = JSON.parse(req.body.passback_params) // Create order createOrder({ out_trade_no: req.body.out_trade_no, trade_no: req.body.trade_no, amount: req.body.total_amount, address: passback.address, products: passback.products, user: passback.userId, result }) } catch (e) { console.error('Signature verification exception:', e) res.status(400).json(e) } } module.exports = { getPayUrl, alipayNotifyUrl }
api - user management - get user historical orders according to id
api design
essential information
Path: /user/orders/:userId
Method: GET
Request parameters
Headers
Parameter name | Is it necessary | remarks |
---|---|---|
Authorization | yes | Authentication token, format bearer < JSON web token > |
Return data
Field name | explain |
---|---|
Response | Returned collection object |
├─ status | Order status |
├─ out_trade_no | Alipay trading ID |
├─ trade_no | Merchant order ID |
├─ amount | Total order amount |
├─ address | Receiving address |
├─ products | Product collection in order |
├─── _id | mongoose default assigned id field |
├─── count | Product purchase quantity |
├─── product | Associated product information (the same as the return object of product details) |
├─── snapshot | Product snapshot information (the same as the return object of product details and filled with product classification information) |
├─ user | User information of the order |
├─── _id | mongoose default assigned id field |
├─── name | User nickname |
├─ createdAt | Specifies the automatically managed createdAt field assigned when creating a Schema |
├─ updatedAt | Specifies the automatically managed updatedAt field assigned when creating a Schema |
├─ _id | mongoose default assigned id field |
├─ _v | mongoose default assigned versionKey field |
Define api
// controllers\user.js ... const Order = require('../models/order') ... // Obtain the user's historical order according to the id const getOrderHistory = (req, res) => { Order.find({ user: req.profile._id }) .select('-products.snapshot.photo') .populate([ { path: 'user', select: '_id name' }, { path: 'products.product', select: '-photo' } ]) .sort('-createdAt') .exec((err, orders) => { if (err) { return res.status(400).json(errorHandler(err)) } res.json(orders) }) } module.exports = { signup, signin, getUserById, read, update, getOrderHistory }
// routes\user.js const { signup, signin, getUserById, read, update, getOrderHistory } = require('../controllers/user') ... // Obtain the user's historical order according to the id router.get('/user/orders/:userId', [tokenParser, authValidator], getOrderHistory) ...
api - order management - get the cover picture of the product snapshot in the order according to the id
api design
essential information
Path: /order/snapshotPhotp/:orderId/:productId
Method: GET
Return data
Returns the file stream of the product snapshot picture
Define api
// controllers\order.js const { errorHandler } = require('../helpers/dbErrorHandler') ... // Obtain the cover picture of the product snapshot in the order according to the id const getSnapshopPhoto = (req, res) => { const { orderId, productId } = req.params Order.findById(orderId) .select('products.snapshot') .exec((err, data) => { if (err || !data) { return res.status(400).json(errorHandler(err)) } const productItem = data.products.find(item => item.snapshot._id.toString() === productId) if (!productItem) { return res.status(400).json(errorHandler('The snapshot information corresponding to the product was not found')) } res.set('Content-Type', productItem.snapshot.photo.contentType) res.send(productItem.snapshot.photo.data) }) } module.exports = { create, createOrder, getSnapshopPhoto }
// routes\order.js const express = require('express') const { create, getSnapshopPhoto } = require('../controllers/order') const router = express.Router() // Create order (skip payment) router.post('/order/create', create) // Obtain the cover picture of the product snapshot in the order according to the id router.get('/order/snapshotPhotp/:orderId/:productId', getSnapshopPhoto) module.exports = router
api - order management - administrator get order list
api design
essential information
Path: /orders/:userId
Method: GET
Request parameters
Headers
Parameter name | Is it necessary | remarks |
---|---|---|
Authorization | yes | Authentication token, format bearer < JSON web token > |
Return data
Returns an array of order information.
Define api
// controllers\order.js ... // Get a list of all orders const list = (req, res) => { Order.find() .select('-products.snapshot.photo') .populate([ { path: 'user', select: '_id name' }, { path: 'products.product', select: '-photo' } ]) .sort('-createdAt') .exec((err, data) => { if (err || !data) { return res.status(400).json(errorHandler(err)) } res.json(data) }) } module.exports = { create, createOrder, getSnapshopPhoto, list }
Configuration api
// routes\order.js const express = require('express') const { create, getSnapshopPhoto, list } = require('../controllers/order') const { tokenParser, authValidator, adminValidator } = require('../validator/user') const { getUserById } = require('../controllers/user') const router = express.Router() // Create order (skip payment) router.post('/order/create', create) // Obtain the cover picture of the product snapshot in the order according to the id router.get('/order/snapshotPhotp/:orderId/:productId', getSnapshopPhoto) // The administrator gets a list of all orders router.get('/orders/:userId', [tokenParser, authValidator, adminValidator], list) // Monitor routing parameters // Obtain user information according to id router.param('userId', getUserById) module.exports = router
api - order management - administrator modifies order status
api design
essential information
Path: /order/updateStatus/:userId
Method: PUT
Request parameters
Headers
Parameter name | Is it necessary | remarks |
---|---|---|
Authorization | yes | Authentication token, format bearer < JSON web token > |
Body
Parameter name | Is it necessary to pass | type | explain |
---|---|---|---|
orderId | yes | String | Order ID |
status | yes | String | Order status |
Return data
Returns a success prompt
Define api
// Modify order status const updateStatus = (req, res) => { const { orderId, status } = req.body if (!orderId) { return res.status(400).json(errorHandler('Please pass in the order ID')) } // Only the save method triggers the verifier // To verify whether the status belongs to the specified enumeration range, save is used instead of updateOne Order.findById(orderId, (err, order) => { if (err || !order) { return res.status(400).json(errorHandler(err)) } order.status = status order.save((error, updateOrder) => { if (error) { return res.status(400).json(errorHandler(error)) } res.json(data) }) }) } module.exports = { create, createOrder, getSnapshopPhoto, list, updateStatus }
// routes\order.js const express = require('express') const { create, getSnapshopPhoto, list, updateStatus } = require('../controllers/order') const { tokenParser, authValidator, adminValidator } = require('../validator/user') const { getUserById } = require('../controllers/user') const router = express.Router() // Create order (skip payment) router.post('/order/create', create) // Obtain the cover picture of the product snapshot in the order according to the id router.get('/order/snapshotPhotp/:orderId/:productId', getSnapshopPhoto) // The administrator gets a list of all orders router.get('/orders/:userId', [tokenParser, authValidator, adminValidator], list) // The administrator modifies the order status router.put('/order/updateStatus/:userId', [tokenParser, authValidator, adminValidator], updateStatus) // Monitor routing parameters // Obtain user information according to id router.param('userId', getUserById) module.exports = router
end
Then you can develop client applications using these interfaces.