From 6a6b6b4f9345b39fb6984a9caa6c53cc468dd9cc Mon Sep 17 00:00:00 2001 From: tai Date: Mon, 31 Oct 2022 21:08:54 +0700 Subject: [PATCH] #100 backend --- .../controllers/carousel.controller.js | 1 - .../carousel.service/carousel.service.js | 2 +- .../Crudcategories.service.js | 2 +- .../feedback.service/feedback.service.js | 58 +++-- .../order.service.js/history.service.js | 18 +- .../payment.service/payment.service.js | 230 ++++++++++-------- .../product.service/getproduct.service.js | 106 ++++---- .../createEditDeleteUser.service.js | 54 ++-- .../user.service/getalluser.service.js | 2 +- .../user.service/newVerification.service.js | 2 +- .../services/user.service/userType.service.js | 10 +- .../services/voucher.service/curd.vouchers.js | 2 +- .../voucher.service/voucher.service.js | 4 +- dump.rdb | Bin 621 -> 14045 bytes 14 files changed, 287 insertions(+), 204 deletions(-) diff --git a/backend/src/v1/user_api/controllers/carousel.controller.js b/backend/src/v1/user_api/controllers/carousel.controller.js index 0eb4356..1951b07 100644 --- a/backend/src/v1/user_api/controllers/carousel.controller.js +++ b/backend/src/v1/user_api/controllers/carousel.controller.js @@ -2,7 +2,6 @@ const { returnReasons } = require("../../middlewares/handleError"); const { handleGetallCarousel, } = require("../services/carousel.service/carousel.service"); -const HELPER = require("../../utils/helper"); const carouselCtrl = { getallCarousel: async (req, res) => { try { diff --git a/backend/src/v1/user_api/services/carousel.service/carousel.service.js b/backend/src/v1/user_api/services/carousel.service/carousel.service.js index 1209b48..f91f9c6 100644 --- a/backend/src/v1/user_api/services/carousel.service/carousel.service.js +++ b/backend/src/v1/user_api/services/carousel.service/carousel.service.js @@ -33,7 +33,7 @@ module.exports = { { $sort: { latest: -1 } }, ]); - await set( + set( "carousel", JSON.stringify(carousels), CONSTANTS._1_DAYS_REDIS + number_random diff --git a/backend/src/v1/user_api/services/category.service/Crudcategories.service.js b/backend/src/v1/user_api/services/category.service/Crudcategories.service.js index b38de6f..cced3cf 100644 --- a/backend/src/v1/user_api/services/category.service/Crudcategories.service.js +++ b/backend/src/v1/user_api/services/category.service/Crudcategories.service.js @@ -11,7 +11,7 @@ module.exports = { const number_random = HELPER.randomNumber(); const category = await Category.find(); - await set( + set( "categories", JSON.stringify(category), CONSTANTS._1_DAYS_REDIS + number_random diff --git a/backend/src/v1/user_api/services/feedback.service/feedback.service.js b/backend/src/v1/user_api/services/feedback.service/feedback.service.js index 4086441..5de862c 100644 --- a/backend/src/v1/user_api/services/feedback.service/feedback.service.js +++ b/backend/src/v1/user_api/services/feedback.service/feedback.service.js @@ -16,7 +16,7 @@ module.exports = { status: 400, success: false, element: { - msg: "Please fill in full infomation", + msg: "Please fill in full information", }, }; } @@ -30,30 +30,40 @@ module.exports = { }, }; } - await sendFeedback( - fullname, - email, - subject, - content, - read_at, - time_log_gmt7_string - ); - - await RedisPub( - "user_feedback", - JSON.stringify({ - email, + return Promise.all([ + sendFeedback( fullname, - content, + email, subject, - }) - ); - return { - status: 200, - success: true, - element: { - msg: "Send Feedback Successfully !!", - }, - }; + content, + read_at, + time_log_gmt7_string + ), + RedisPub( + "user_feedback", + JSON.stringify({ + email, + fullname, + content, + subject, + }) + ) + ]).then(result => { + return { + status: 200, + success: true, + element: { + msg: "Send Feedback Successfully !!", + }, + }; + }).catch((err) => { + return { + status: 400, + success: true, + element: { + msg: "Send Feedback Fail !!", + }, + }; + }) }, }; diff --git a/backend/src/v1/user_api/services/order.service.js/history.service.js b/backend/src/v1/user_api/services/order.service.js/history.service.js index d02b554..374b928 100644 --- a/backend/src/v1/user_api/services/order.service.js/history.service.js +++ b/backend/src/v1/user_api/services/order.service.js/history.service.js @@ -27,7 +27,7 @@ module.exports = { }, handleDeleteFlagOrders: async ({ order_id }) => { try { - await Payments.findOneAndUpdate( + Payments.findOneAndUpdate( { _id: order_id }, { deleteAt: CONSTANTS.DELETED_ENABLE, @@ -41,7 +41,13 @@ module.exports = { }, }; } catch (error) { - return res.status(500).json({ msg: err.message }); + return { + status: 503, + success: false, + element: { + msg: "Server busy !!", + }, + }; } }, handleDetailOrders: async ({ order_id }) => { @@ -55,7 +61,13 @@ module.exports = { }, }; } catch (error) { - return res.status(500).json({ msg: err.message }); + return { + status: 503, + success: false, + element: { + msg: "Server busy !!", + }, + }; } }, }; diff --git a/backend/src/v1/user_api/services/payment.service/payment.service.js b/backend/src/v1/user_api/services/payment.service/payment.service.js index 896a6ac..d5862c4 100644 --- a/backend/src/v1/user_api/services/payment.service/payment.service.js +++ b/backend/src/v1/user_api/services/payment.service/payment.service.js @@ -10,125 +10,155 @@ const Products = require("../../../models/ProductModel"); const STORAGE = require("../../../utils/storage"); module.exports = { handlePaymentTotal: async ({ user_id }) => { - if (!user_id) { + try { + if (!user_id) { + return { + status: 400, + success: false, + element: { + msg: "User Fail !", + }, + }; + } + const data = await hgetall(user_id); + const quantity_sum = await sumQuantity({ user_id }); + + var cart = []; + for (var key in data) { + cart.push({ + cart: await Products.find({ _id: key }), + quantity_sum, + }); + } + let total = 0; + let total_apply_voucher = 0; + const voucher = await get(`voucher_userId:${user_id}`); + for (let i = 0; i < cart.length; i++) { + total += cart[i].cart[0].price * cart[i].quantity_sum; + } + total_apply_voucher = (total * JSON.parse(voucher)) / 100; + return { + status: 200, + success: true, + element: { + total, + voucher: JSON.parse(voucher), + total_apply_voucher, + }, + }; + } catch (error) { return { - status: 400, + status: 503, success: false, element: { - msg: "User Fail !", + msg: "Server busy !!", }, }; } - const data = await hgetall(user_id); - const quantity_sum = await sumQuantity({ user_id }); - - var cart = []; - for (var key in data) { - cart.push({ - cart: await Products.find({ _id: key }), - quantity_sum, - }); - } - let total = 0; - let total_apply_voucher = 0; - const voucher = await get(`voucher_userId:${user_id}`); - for (let i = 0; i < cart.length; i++) { - total += cart[i].cart[0].price * cart[i].quantity_sum; - } - total_apply_voucher = (total * JSON.parse(voucher)) / 100; - return { - status: 200, - success: true, - element: { - total, - voucher: JSON.parse(voucher), - total_apply_voucher, - }, - }; }, handleCheckInStock: async ({ user_id }) => { - const data = await hgetall(user_id); - var cart = []; - for (var key in data) { - cart.push({ - cart: await Products.find({ _id: key }), - }); - } - let stockAvailable = true; - let outOfStock = []; - for (let i = 0; i < cart.length; i++) { - if (cart[i].cart[0].countInStock === 0) { - stockAvailable = false; - outOfStock.push({ - outOfStock: cart[i].cart[0], + try { + const data = await hgetall(user_id); + var cart = []; + for (var key in data) { + cart.push({ + cart: await Products.find({ _id: key }), }); } + let stockAvailable = true; + let outOfStock = []; + for (let i = 0; i < cart.length; i++) { + if (cart[i].cart[0].countInStock === 0) { + stockAvailable = false; + outOfStock.push({ + outOfStock: cart[i].cart[0], + }); + } + } + return { + status: 200, + success: true, + element: { + stockAvailable, + outOfStock, + }, + }; + } catch (error) { + return { + status: 503, + success: false, + element: { + msg: "Server busy !!", + }, + }; } - return { - status: 200, - success: true, - element: { - stockAvailable, - outOfStock, - }, - }; }, handlePaymentPaypal: async ({ user_id, paymentID, address }) => { - const data = await hgetall(user_id); - var cart = []; - for (var key in data) { - cart.push({ - cart: await Products.find({ _id: key }), - quantity: data[key], + try { + const data = await hgetall(user_id); + var cart = []; + for (var key in data) { + cart.push({ + cart: await Products.find({ _id: key }), + quantity: data[key], + }); + } + let total = 0; + let total_apply_voucher = 0; + const voucher = await get(`voucher_userId:${user_id}`); + for (let i = 0; i < cart.length; i++) { + total += cart[i].cart[0].price * cart[i].quantity; + } + total_apply_voucher = (total * JSON.parse(voucher)) / 100; + const { success, element } = await createPayment({ + user_id, + cart, + paymentID, + address, + total, + total_apply_voucher, + voucher: JSON.parse(voucher), }); - } - let total = 0; - let total_apply_voucher = 0; - const voucher = await get(`voucher_userId:${user_id}`); - for (let i = 0; i < cart.length; i++) { - total += cart[i].cart[0].price * cart[i].quantity; - } - total_apply_voucher = (total * JSON.parse(voucher)) / 100; - const { success, element } = await createPayment({ - user_id, - cart, - paymentID, - address, - total, - total_apply_voucher, - voucher: JSON.parse(voucher), - }); - if (!success) { + if (!success) { + return { + status: 400, + success: false, + element: { + msg: "Payment Paypal Fail !!!", + }, + }; + } + RedisPub( + "user_payment_success", + JSON.stringify({ + email: element?.email, + name: element?.name, + }) + ); + for (let i = 0; i < cart.length; i++) { + STORAGE.sold(cart[i].cart[0]._id, cart[i].quantity, cart[i].cart[0].sold); + STORAGE.stock( + cart[i].cart[0]._id, + cart[i].quantity, + cart[i].cart[0].countInStock + ); + } + del(`cartUserId:${user_id}`); return { - status: 400, + status: 200, + success: true, + element: { + msg: "Payment Paypal Success !!", + }, + }; + } catch (error) { + return { + status: 503, success: false, element: { - msg: "Payment Paypal Fail !!!", + msg: "Server busy !!", }, }; } - await RedisPub( - "user_payment_success", - JSON.stringify({ - email: element?.email, - name: element?.name, - }) - ); - for (let i = 0; i < cart.length; i++) { - STORAGE.sold(cart[i].cart[0]._id, cart[i].quantity, cart[i].cart[0].sold); - STORAGE.stock( - cart[i].cart[0]._id, - cart[i].quantity, - cart[i].cart[0].countInStock - ); - } - await del(`cartUserId:${user_id}`); - return { - status: 200, - success: true, - element: { - msg: "Payment Paypal Success !!", - }, - }; }, }; diff --git a/backend/src/v1/user_api/services/product.service/getproduct.service.js b/backend/src/v1/user_api/services/product.service/getproduct.service.js index ab3c950..d58f379 100644 --- a/backend/src/v1/user_api/services/product.service/getproduct.service.js +++ b/backend/src/v1/user_api/services/product.service/getproduct.service.js @@ -4,54 +4,74 @@ const Products = require("../../../models/ProductModel"); const { get, set } = require("../../../utils/limited_redis"); module.exports = { async getallProductUser() { - const product_user_redis = await get("product_user"); - if (product_user_redis) { - return JSON.parse(product_user_redis); - } - const number_random = HELPER.randomNumber(); - const product_user = await Products.aggregate([ - { - $project: { - _id: "$_id", - name: "$name", - price: "$price", - image: "$image", - description: "$description", - rating: "$rating", - numReviews: "$numReviews", - sold: "$sold", - countInStock: "$countInStock", - categories: "$categories", - latest: { - $cond: { - if: { $gt: ["$createdAt", "$updatedAt"] }, - then: "$createdAt", - else: "$updatedAt", + try { + const product_user_redis = await get("product_user"); + if (product_user_redis) { + return JSON.parse(product_user_redis); + } + const number_random = HELPER.randomNumber(); + const product_user = await Products.aggregate([ + { + $project: { + _id: "$_id", + name: "$name", + price: "$price", + image: "$image", + description: "$description", + rating: "$rating", + numReviews: "$numReviews", + sold: "$sold", + countInStock: "$countInStock", + categories: "$categories", + latest: { + $cond: { + if: { $gt: ["$createdAt", "$updatedAt"] }, + then: "$createdAt", + else: "$updatedAt", + }, }, }, }, - }, - { $sort: { latest: -1 } }, - ]); - await set( - "product_user", - JSON.stringify(product_user), - CONSTANTS._1_DAYS_REDIS + number_random - ); - return product_user; + { $sort: { latest: -1 } }, + ]); + set( + "product_user", + JSON.stringify(product_user), + CONSTANTS._1_DAYS_REDIS + number_random + ); + return product_user; + } catch (error) { + return { + status: 503, + success: false, + element: { + msg: "Server busy !!", + }, + }; + } }, async getallProductUserDetail(product_id) { - var product_user_redis = await get("product_user"); - product_user_redis = JSON.parse(product_user_redis); - if (product_user_redis) { - return product_user_redis.filter((x) => x._id === product_id); + try { + var product_user_redis = await get("product_user"); + product_user_redis = JSON.parse(product_user_redis); + if (product_user_redis) { + return product_user_redis.filter((x) => x._id === product_id); + } + const product_detail = await Products.findById(product_id).populate({ + path: "reviews", + populate: { + path: "user", + }, + }); + return product_detail; + } catch (error) { + return { + status: 503, + success: false, + element: { + msg: "Server busy !!", + }, + }; } - const product_detail = await Products.findById(product_id).populate({ - path: "reviews", - populate: { - path: "user", - }, - }); - return product_detail; }, }; diff --git a/backend/src/v1/user_api/services/user.service/createEditDeleteUser.service.js b/backend/src/v1/user_api/services/user.service/createEditDeleteUser.service.js index 3231691..ceb991c 100644 --- a/backend/src/v1/user_api/services/user.service/createEditDeleteUser.service.js +++ b/backend/src/v1/user_api/services/user.service/createEditDeleteUser.service.js @@ -9,7 +9,7 @@ const { // ** Delete Verification const deleteVerification = async (userId) => { - await UserVerifications.deleteOne({ userId }); + UserVerifications.deleteOne({ userId }); }; //** Create User */ const createUser = async ( @@ -39,8 +39,7 @@ const UpdateProfile = async ({ date_of_birth, user_id, }) => { - await deleteImageAutoCloud(user_id); - await Users.findOneAndUpdate( + return Promise.all([deleteImageAutoCloud(user_id), Users.findOneAndUpdate( { _id: user_id }, { name, @@ -49,30 +48,40 @@ const UpdateProfile = async ({ sex, date_of_birth, } - ); - let userId = user_id; - - if (userId) { - await updateProfileId(userId); - } - return true; + ), + updateProfileId(user_id) + ]).then(() => { + return true; + }).catch(err => { + return false; + }) }; //** Delete User And Verification */ const deleteVerificationAndUser = async (userId) => { - await UserVerifications.deleteOne({ userId }); - await Users.deleteOne({ _id: userId }); - return true; + return Promise.all([ + UserVerifications.deleteOne({ userId }), Users.deleteOne({ _id: userId }) + ]).then((rs) => { + return true; + }).catch(err => { + return false; + }) }; //** Update Verification CheckEmail */ const UpdateVerificationUser = async (userId) => { - await Users.findOneAndUpdate( - { _id: userId }, - { - verified: CONSTANTS.DELETED_ENABLE, - checkLogin: CONSTANTS.DELETED_ENABLE, - } - ); - await deleteVerification(userId); + + return Promise.all([ + Users.findOneAndUpdate( + { _id: userId }, + { + verified: CONSTANTS.DELETED_ENABLE, + checkLogin: CONSTANTS.DELETED_ENABLE, + } + ), deleteVerification(userId) + ]).then((rs) => { + return true; + }).catch(err => { + return false; + }) return true; }; //** Create_acceptToken */ @@ -90,7 +99,8 @@ const UpdatePassword = async ({ user_id, password }) => { //** Delete Image Cloud auto */ const deleteImageAutoCloud = async (user_id) => { const user = await Users.findById(user_id); - return await destroyStorage(user?.image?.public_id); + let public_id = user?.image?.public_id + return destroyStorage(public_id); }; module.exports = { //* CreateUser diff --git a/backend/src/v1/user_api/services/user.service/getalluser.service.js b/backend/src/v1/user_api/services/user.service/getalluser.service.js index 897755f..e1d4cef 100644 --- a/backend/src/v1/user_api/services/user.service/getalluser.service.js +++ b/backend/src/v1/user_api/services/user.service/getalluser.service.js @@ -12,7 +12,7 @@ const getProfileId = async (userId) => { const random_number = HELPER.randomNumber(); const user = await Users.findById(userId).select("+password"); if (user) { - await set( + set( `userId:${userId}`, JSON.stringify(user), CONTAINS._1_DAYS_REDIS + random_number diff --git a/backend/src/v1/user_api/services/user.service/newVerification.service.js b/backend/src/v1/user_api/services/user.service/newVerification.service.js index 9dddb07..dde8f04 100644 --- a/backend/src/v1/user_api/services/user.service/newVerification.service.js +++ b/backend/src/v1/user_api/services/user.service/newVerification.service.js @@ -8,6 +8,6 @@ module.exports = { createdAt: Date.now(), expiresAt: Date.now() + CONSTANTS._45_MINUTES, }); - return await newVerification.save(); + return newVerification.save(); }, }; diff --git a/backend/src/v1/user_api/services/user.service/userType.service.js b/backend/src/v1/user_api/services/user.service/userType.service.js index c0d0d9e..25931e6 100644 --- a/backend/src/v1/user_api/services/user.service/userType.service.js +++ b/backend/src/v1/user_api/services/user.service/userType.service.js @@ -82,15 +82,17 @@ module.exports = { }, verified: true, }); - await newUser.save(); - await RedisPub( + return Promise.all([newUser.save(), RedisPub( "user_register_password_google_facebook", JSON.stringify({ password, name, email, }) - ); - return newUser; + )]).then(rs => { + return newUser; + }).catch(err => { + return err; + }); }, }; diff --git a/backend/src/v1/user_api/services/voucher.service/curd.vouchers.js b/backend/src/v1/user_api/services/voucher.service/curd.vouchers.js index b75e382..a2c2ed2 100644 --- a/backend/src/v1/user_api/services/voucher.service/curd.vouchers.js +++ b/backend/src/v1/user_api/services/voucher.service/curd.vouchers.js @@ -1,6 +1,6 @@ const Voucher = require("../../../models/VoucherModel"); const checkVoucher = async (title) => { - return await Voucher.findOne({ title }); + return Voucher.findOne({ title }); }; module.exports = { checkVoucher, diff --git a/backend/src/v1/user_api/services/voucher.service/voucher.service.js b/backend/src/v1/user_api/services/voucher.service/voucher.service.js index 5be88a7..629933c 100644 --- a/backend/src/v1/user_api/services/voucher.service/voucher.service.js +++ b/backend/src/v1/user_api/services/voucher.service/voucher.service.js @@ -12,7 +12,7 @@ module.exports = { msg: "This Voucher already exists.", }, }; - await set( + set( `voucher_userId:${user_id}`, check_voucher.value, CONSTANTS._15_MINUTES_REDIS @@ -40,7 +40,7 @@ module.exports = { }, }; } - await del(`voucher_userId:${user_id}`); + del(`voucher_userId:${user_id}`); return { status: 200, success: true, diff --git a/dump.rdb b/dump.rdb index caebd0f95fce178fc6d2eff1335e0ca5a241ccc9..43770e0fc25233ba96dc6d2c96ba4d715edf6660 100644 GIT binary patch literal 14045 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