'use strict'; const shopController = require('./shop.js'); // 订单控制器 module.exports = class OrderController extends shopController { /** * [useModel 使用模型] * @return {[type]} [description] */ get useModel() { const that = this; return that.app.model.Orders; } /** * [addressModel 用户地址模型] * @return {[type]} [description] */ get useAddressModel() { const that = this; return that.app.model.Address; } /** * [useCartModel 使用购物车模型] * @return {[type]} [description] */ get useCartModel() { const that = this; return that.app.model.Carts; } /** * [usePaysConfigModel 使用支付模型] * @return {[type]} [description] */ get usePaysConfigModel() { const that = this; return that.app.model.PaysConfig; } /** * [useOrdersProductsModel 订单商品模型] * @return {[type]} [description] */ get useOrdersProductsModel() { const that = this; return that.app.model.OrdersProducts; } /** * [createValidate 用户下单验证器] * @return {[type]} [description] */ get createValidate() { const that = this; return { pay_id: that.ctx.rules.name('支付方式') .required() .notEmpty() .number(), pid: that.ctx.rules.name('家具分类父级ID') .required() .notEmpty() .number(), user_id: that.ctx.rules.default(that.service.shop.getWebUserId()) .number(), user_remarks: that.ctx.rules.default('') .required(), order_source: that.ctx.rules.default(1) .number(), order_sn: that.ctx.rules.default(`W${that.app.szjcomo.date('YmdHis')}${that.app.szjcomo.mt_rand(100000, 999999)}`) .required(), create_time: that.ctx.rules.default(that.app.szjcomo.date('Y-m-d H:i:s')) .required(), }; } /** * [selectValidate 获取订单列表] * @return {[type]} [description] */ get selectValidate() { const that = this; return { page: that.ctx.rules.default(1) .number(), limit: that.ctx.rules.default(20) .number(), user_id: that.ctx.rules.default(that.service.shop.getWebUserId()) .number(), order_status: that.ctx.rules.default(-1) .number(), order_id: that.ctx.rules.default(0) .number(), comment_id: that.ctx.rules.default(0) .number(), }; } /** * [selectInfoValidate 订单详情验证器] * @return {[type]} [description] */ get selectInfoValidate() { const that = this; return { order_id: that.ctx.rules.name('订单ID') .required() .notEmpty() .number(), }; } /** * [orderCountValidate 获取订单数量统计] * @return {[type]} [description] */ get orderCountValidate() { const that = this; return { user_id: that.ctx.rules.default(that.service.shop.getWebUserId()) .number(), }; } /** * [cancelValidate 用户取消订单] * @return {[type]} [description] */ get cancelValidate() { const that = this; return { user_id: that.ctx.rules.default(that.service.shop.getWebUserId()) .number(), order_id: that.ctx.rules.name('订单ID') .required() .notEmpty() .number(), }; } /** * [deliverValidate 查看物流配送信息] * @return {[type]} [description] */ get deliverValidate() { const that = this; return { order_id: that.ctx.rules.name('订单ID') .required() .notEmpty() .number(), user_id: that.ctx.rules.default(that.service.shop.getWebUserId()) .number(), }; } /** * [orderCount 订单数量统计] * @return {[type]} [description] */ async orderCount() { const that = this; try { const data = await that.ctx.validate(that.orderCountValidate, await that.ctx.getParse()); const seq = that.app.Sequelize; const options = { where: { user_id: data.user_id, order_status: { [seq.Op.in]: [ 0, 1, 2, 3 ], }, }, group: [ 'order_status' ], }; const result = await that.useModel.count(options); return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', result, false)); } catch (err) { return that.ctx.appJson(that.app.szjcomo.appResult(err.message)); } } /** * [select 查询订单] * @return {[type]} [description] */ async select() { const that = this; try { const data = await that.ctx.validate(that.selectValidate, await that.ctx.getParse()); let result; if (data.order_id) { result = await that.orderInfo(data); } else { result = await that.orderList(data); } return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', result, false)); } catch (err) { return that.ctx.appJson(that.app.szjcomo.appResult(err.message)); } } /** * [selectInfo 查询订单详情] * @return {[type]} [description] */ async selectInfo() { const that = this; try { const data = await that.ctx.validate(that.selectInfoValidate, await that.ctx.getParse()); const result = await that.orderInfo(data); return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', result, false)); } catch (err) { return that.ctx.appJson(that.app.szjcomo.appResult(err.message)); } } /** * [orderInfo 订单详情] * @return {[type]} [description] */ async orderInfo(data = {}) { const that = this; const seq = that.app.Sequelize; let commentInclude = []; if (data.comment_id) { commentInclude = [ { model: that.app.model.ProductComment, as: 'product_comment', attributes: [ 'comment' ], where: { order_id: data.order_id }, required: false, } ]; } const options = { where: { order_id: data.order_id }, include: [ { model: that.app.model.OrdersProducts, as: 'orders_products', attributes: [ 'product_count', 'product_name', 'product_image', 'product_id', 'category_id', 'shop_price', 'volume', 'price' ], include: commentInclude, }, { model: that.app.model.PaysConfig, as: 'pays_config', attributes: [] }, ], attributes: [ 'order_sn', 'create_time', 'order_status', 'order_amount', 'order_id', [ seq.col('pays_config.pay_name'), 'pay_name' ], 'consignee', 'mobile', 'address', 'is_urge', 'pay_id', 'surplus_amout' ], }; const selectBean = await that.app.comoBean.instance(data, options); const result = await that.service.base.select(selectBean, that.useModel, '订单详情查询失败,请稍候重试', false); return result; } /** * [orderList 订单列表] * @param {Object} data [description] * @return {[type]} [description] */ async orderList(data = {}) { const that = this; // const seq = that.app.Sequelize; const options = { offset: (data.page - 1) * data.limit, limit: data.limit, where: { user_id: data.user_id }, include: [ { model: that.app.model.OrdersProducts, as: 'orders_products', attributes: [ 'product_count', 'product_name', 'product_image', 'product_id', 'category_id', 'shop_price', 'volume', 'price' ], order: [ [ 'rec_id', 'asc' ] ], }, ], attributes: [ 'order_sn', 'create_time', 'order_status', 'order_amount', 'surplus_amout', 'order_id', 'is_urge' ], order: [ [ 'order_id', 'desc' ] ], }; if (data.order_status > -1) options.where.order_status = data.order_status; const selectBean = await that.app.comoBean.instance(data, options); const result = await that.service.base.select(selectBean, that.useModel, '订单列表查询失败,请稍候重试', true); return result; } /** * [cancel 用户取消订单] * @return {[type]} [description] */ async cancel() { const that = this; let transaction; try { const data = await that.ctx.validate(that.cancelValidate, await that.ctx.anyParse()); transaction = await that.app.model.transaction(); const info = await that.useModel.findOne({ where: { order_id: data.order_id, user_id: data.user_id }, attributes: [ 'surplus_amout', 'order_sn' ], transaction, raw: true, }); if (!info) throw new Error('订单信息不存在,请勿非法操作'); const opts = { where: { user_id: data.user_id, order_id: data.order_id }, transaction }; const updateBean = await that.app.comoBean.instance({ order_status: 6, surplus_amout: 0 }, opts); const result = await that.service.base.update(updateBean, that.useModel, '取消订单操作失败,请重试'); if (Number(info.surplus_amout) > 0) await that.service.shop.userMoneyAdd(data.user_id, Number(info.surplus_amout), transaction, `订单取消,退回订单抵扣的余额,订单编号是:${info.order_sn}`); await that.service.order.orderAction({ admin_id: 0, order_id: data.order_id, action_desc: '用户取消订单', }, transaction); await transaction.commit(); return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', result, false)); } catch (err) { if (transaction) await transaction.rollback(); return that.ctx.appJson(that.app.szjcomo.appResult(err.message)); } } /** * [urge 用户催发货] * @return {[type]} [description] */ async urge() { const that = this; let transaction; try { const data = await that.ctx.validate(that.cancelValidate, await that.ctx.anyParse()); transaction = await that.app.model.transaction(); const opts = { where: { user_id: data.user_id, order_id: data.order_id }, transaction }; const updateBean = await that.app.comoBean.instance({ is_urge: that.app.Sequelize.literal('is_urge + 1') }, opts); const result = await that.service.base.update(updateBean, that.useModel, '催发货失败,请稍候重试'); await that.service.order.orderAction({ admin_id: 0, order_id: data.order_id, action_desc: '用户催发货', }, transaction); await transaction.commit(); return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', result, false)); } catch (err) { if (transaction) await transaction.rollback(); return that.ctx.appJson(that.app.szjcomo.appResult(err.message)); } } /** * [confirm 用户确认收货] * @return {[type]} [description] */ async confirm() { const that = this; let transaction; try { const data = await that.ctx.validate(that.cancelValidate, await that.ctx.anyParse()); transaction = await that.app.model.transaction(); const opts = { where: { user_id: data.user_id, order_id: data.order_id }, transaction }; const updateBean = await that.app.comoBean.instance({ order_status: 3, query_time: that.app.szjcomo.date('Y-m-d H:i:s'), }, opts); const result = await that.service.base.update(updateBean, that.useModel, '确认收货更新失败,请稍候重试'); await that.service.order.orderAction({ admin_id: 0, order_id: data.order_id, action_desc: '用户已收货', }, transaction); await transaction.commit(); return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', result, false)); } catch (err) { if (transaction) await transaction.rollback(); return that.ctx.appJson(that.app.szjcomo.appResult(err.message)); } } // ======================以下为用户下单所需要方法============================== /** * [createV2 用户下单] * @return {[type]} [description] */ async createV2() { const that = this; let transaction; try { const data = await that.ctx.validate(that.createValidate, await that.ctx.postParse()); const inviteCode = data.inviteCode ? Number(data.inviteCode) : -1; const isUseMoney = data.isUseMoney ? data.isUseMoney : false; that.logger.error('用户下单绑定的邀请码: %s ', inviteCode); transaction = await that.app.model.transaction(); const products = await that.getCartProducts(data.user_id, data.pid, transaction); const orderAmount = await that.caclOrderAmountV2(products, data.pid); const result = await that.orderCreateBeforeV2(data, orderAmount, transaction, inviteCode, isUseMoney); const cartIds = await that.orderProductsAfter(result.order.order_id, products, data, transaction); await that.clearCartProducts(cartIds, transaction); await transaction.commit(); return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', result.payParmas, false)); } catch (err) { if (transaction) await transaction.rollback(); await that.logs('order.js', err); return that.ctx.appJson(that.app.szjcomo.appResult(err.message)); } } /** * [caclOrderAmount 计算订单总价] * @return {[type]} [description] * @param products * @param pid */ async caclOrderAmountV2(products, pid = 4) { const that = this; const keys = [ 'can_discount_category' ]; const config = await that.service.configs.getConfigMoreValue(keys); const temA = config.can_discount_category.replace('[', ''); const temB = temA.replace(']', ''); let canDiscountCategory = temB.split(','); canDiscountCategory = canDiscountCategory.map(Number); const orderAmount = { orderPriceCanDiscount: 0, orderPriceNotDiscount: 0 }; // 2023/10/12 todo 订单价格办公家具全场八折 products.forEach(item => { if (!canDiscountCategory.includes(item.category_id)) { orderAmount.orderPriceNotDiscount += ((Number(item.price > 0 ? item.price : item.shop_price) * item.product_count) * (pid === 1 ? 0.8 : 1)); } else { orderAmount.orderPriceCanDiscount += ((Number(item.price > 0 ? item.price : item.shop_price) * item.product_count) * (pid === 1 ? 0.8 : 1)); } }); return orderAmount; } /** * [orderCreateBefore 用户下单前置] * @param data data [description] * @param orderAmount * @param transaction * @param inviteCode * @param isUseMoney * @return {[type]} [description] */ async orderCreateBeforeV2(data = {}, orderAmount = { orderPriceCanDiscount: 0, orderPriceNotDiscount: 0, }, transaction = null, inviteCode = -1, isUseMoney = false) { const that = this; const address = await that.getUserAddress(data.user_id, transaction); if (!address && !data.isSelfPickUp) throw new Error('您还没有完善收货信息,请完善收货地址或者选择门店自提再下单吧!'); // 计算用户需要支付的金额 const tmpPayMentMoney = await that.service.order.caclPaymentMoneyV2(data, orderAmount, transaction, isUseMoney); const opts = { order_sn: data.order_sn, user_id: data.user_id, order_status: 0, order_source: data.order_source, surplus_amout: tmpPayMentMoney.surplus_amout, consignee: address ? address.uname : '门店自提', mobile: address ? address.mobile : '', order_amount: orderAmount.orderPriceCanDiscount + orderAmount.orderPriceNotDiscount, province_id: address ? address.province_id : '', city_id: address ? address.city_id : '', user_remarks: data.user_remarks, county_id: address ? address.county_id : '', address: address ? [ address.province_name, address.city_name, address.county_name, address.address ].join('') : '门店自提', create_time: data.create_time, pay_id: data.pay_id, shipping_fee: 0, is_urge: 0, inviter_id: inviteCode, }; const orderBean = await that.app.comoBean.instance(opts, { transaction }); // 2022/11/11 创建订单 const order = await that.service.base.create(orderBean, that.useModel, '订单提交失败,请重试'); if (tmpPayMentMoney.surplus_amout) await that.service.shop.userMoneySub(data.user_id, tmpPayMentMoney.surplus_amout, transaction, `商品下单时余额抵扣,订单编号是:${data.order_sn}`); // 用户全部使用余额支付 if (!tmpPayMentMoney.payment_money) { const payParmas = await that.service.order.getSurplusPayParams(order.order_id, data.user_id, tmpPayMentMoney.surplus_amout, transaction); // 订单发货时扣除库存,受控于后台配置的扣除时机 await that.service.order.productStockSub(order.order_id, transaction, 1); return { order, payParmas }; } let payParmas = {}; const userInfo = await that.app.model.Users.findOne({ where: { user_id: data.user_id }, attributes: [ 'openid' ], transaction, raw: true, }); if (data.order_source === 1) { payParmas = await that.service.wechat.wechatAccountJsApiPay({ body: '订单支付', openid: userInfo.openid, total_fee: (tmpPayMentMoney.payment_money * 100), trade_type: 'JSAPI', attach: that.app.szjcomo.base64_encode(that.app.szjcomo.json_encode({ user_id: data.user_id, order_id: order.order_id, pay_id: data.pay_id, })), }); } return { order, payParmas: { payment_money: tmpPayMentMoney.payment_money, params: payParmas, order_id: order.order_id }, }; } /** * [orderProductsAfter description] * @param {[type]} order_id [description] * @param {Array} products [description] * @param data * @param {[type]} transaction [description] * @return {[type]} [description] */ async orderProductsAfter(order_id, products = [], data = {}, transaction = null) { const that = this; const cartIds = []; // const seq = that.app.Sequelize; const orderProducts = products.map(item => { const tmp = Object.assign({ order_id, total_price: Number(item.price > 0 ? item.price : item.shop_price) * item.product_count, // 2023/1/9 默认出货数量是订单商品的购买数量 deliver_count: Number(item.product_count), create_time: data.create_time, }, item); cartIds.push(item.cart_id); return tmp; }); const result = await that.useOrdersProductsModel.bulkCreate(orderProducts, { transaction }); if (!result) throw new Error('订单商品写入失败,请重试'); return cartIds; } /** * [clearCartProducts 清除购物车商品] * @param {Array} cartIds [description] * @return {[type]} [description] */ async clearCartProducts(cartIds = [], transaction = null) { const that = this; const seq = that.app.Sequelize; const deleteBean = await that.app.comoBean.instance({}, { where: { cart_id: { [seq.Op.in]: cartIds } }, transaction, }); await that.service.base.delete(deleteBean, that.useCartModel, '订单提交成功,清除购物车失败,请重试'); } /** * [getCartProducts 下单时获取购物车商品] * @param {Number} user_id [description] * @return {[type]} [description] */ async getCartProducts(user_id = 0, pid = 4, transaction = null) { const that = this; const seq = that.app.Sequelize; const options = { where: { user_id, is_select: 1, pid }, transaction, raw: true, include: [ { model: that.app.model.Products, as: 'products', attributes: [] }, ], attributes: [ [ seq.col('products.product_id'), 'product_id' ], [ seq.col('products.category_id'), 'category_id' ], [ seq.col('products.product_name'), 'product_name' ], [ seq.col('products.product_sn'), 'product_sn' ], [ seq.col('products.product_image'), 'product_image' ], [ seq.col('products.shop_price'), 'shop_price' ], [ seq.col('products.dinning_coin_amount'), 'dinning_coin_amount' ], [ seq.col('products.dining_partner_id'), 'dining_partner_id' ], [ seq.col('products.goods_type'), 'goods_type' ], [ seq.col('products.activity_id'), 'activity_id' ], 'product_count', 'cart_id', 'volume', 'price', ], }; const res = await that.useCartModel.findAll(options); return res; } /** * [getUserAddress 获取收货地址] * @param {Number} user_id [description] * @return {[type]} [description] */ async getUserAddress(user_id = 0, transaction = null) { const that = this; const options = { where: { user_id, is_use: 1 }, raw: true, transaction, attributes: { exclude: [ 'user_id', 'address_id', 'is_use', 'update_time', 'create_time' ], }, }; const res = await that.useAddressModel.findOne(options); return res; } /** * [orderDeliver 查看物流配送信息] * @return {[type]} [description] */ async orderDeliver() { const that = this; try { const data = await that.ctx.validate(that.deliverValidate, await that.ctx.getParse()); const selectBean = await that.app.comoBean.instance(data, { where: { order_id: data.order_id, user_id: data.user_id }, include: [ { model: that.app.model.OrdersProducts, as: 'orders_products', attributes: [ 'product_count', 'product_name', 'product_image', 'product_id', 'category_id', 'shop_price', 'deliver_count', 'is_deliver', 'volume', 'price' ], }, ], attributes: [ 'order_sn', 'create_time', 'order_status', 'order_amount', 'order_id', 'consignee', 'mobile', 'address', 'is_urge', 'pay_id', 'surplus_amout', 'is_express', 'express_sn', 'express_id', 'deliver_desc', 'deliver_time' ], }); const result = await that.service.base.select(selectBean, that.useModel, '查询订单详情失败,请稍候重试', false, false); return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', result, false)); } catch (err) { return that.ctx.appJson(that.app.szjcomo.appResult(err.message)); } } /** * [orderPayFind 查询验证订单是否已经支付] * @return {[type]} [description] */ async orderPayFind() { const that = this; try { const data = await that.ctx.validate(that.cancelValidate, await that.ctx.getParse()); const selectBean = await that.app.comoBean.instance(data, { where: { order_id: data.order_id, user_id: data.user_id, }, attributes: [ 'order_status' ], }); const result = await that.service.base.select(selectBean, that.useModel, '查询订单信息失败,请勿非法操作', false, false); return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', result, false)); } catch (err) { return that.ctx.appJson(that.app.szjcomo.appResult(err.message)); } } /** * [orderAgainPay 订单继续支付] * @return {[type]} [description] */ async orderAgainPay() { const that = this; try { const data = await that.ctx.validate(that.cancelValidate, await that.ctx.getParse()); const seq = that.app.Sequelize; const info = await that.useModel.findOne({ include: [ { model: that.app.model.Users, as: 'users', attributes: [] } ], where: { order_id: data.order_id }, raw: true, attributes: [ 'order_amount', 'surplus_amout', 'pay_id', [ seq.col('users.openid'), 'openid' ] ], }); if (!info) throw new Error('订单信息不存在,请勿非法操作'); const result = await that.service.wechat.wechatAccountJsApiPay({ body: '订单支付', openid: info.openid, total_fee: ((Number(info.order_amount) - Number(info.surplus_amout)) * 100), trade_type: 'JSAPI', attach: that.app.szjcomo.base64_encode(that.app.szjcomo.json_encode({ user_id: data.user_id, order_id: data.order_id, pay_id: data.pay_id, })), }); return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', result, false)); } catch (err) { return that.ctx.appJson(that.app.szjcomo.appResult(err.message)); } } };