123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 |
- 'use strict';
- const { CrudService } = require('naf-framework-mongoose-free/lib/service');
- const { BusinessError, ErrorCode } = require('naf-core').Error;
- const _ = require('lodash');
- const assert = require('assert');
- const moment = require('moment');
- const Transaction = require('mongoose-transactions');
- //
- class OrderService extends CrudService {
- constructor(ctx) {
- super(ctx, 'order');
- this.redis = this.app.redis;
- this.redisKey = this.app.config.redisKey;
- this.model = this.ctx.model.Trade.Order;
- this.goodsModel = this.ctx.model.Shop.Goods;
- this.goodsSpecModel = this.ctx.model.Shop.GoodsSpec;
- this.addressModel = this.ctx.model.User.Address;
- this.cartModel = this.ctx.model.Trade.Cart;
- this.tran = new Transaction();
- }
- /**
- * 创建订单
- * 1.检测商品是否可以购买
- * 2.数据做快照处理
- * @param {Object} body
- */
- async create(body) {
- // 声明事务
- try {
- const user = this.ctx.user;
- const customer = _.get(user, '_id');
- if (!customer) throw new BusinessError(ErrorCode.NOT_LOGIN, '未找到用户信息');
- const { address, goods, total_detail } = body;
- // 检测商品是否可以下单
- for (const i of goods) {
- const { shop } = i;
- for (const g of i.goods) {
- const { goods_id: goods, goodsSpec_id: goodsSpec, num } = g;
- const { result, msg } = await this.ctx.service.util.trade.checkCanBuy({ shop, goods, goodsSpec, num }, false);
- if (!result) throw new BusinessError(ErrorCode.DATA_INVALID, msg);
- }
- }
- const orderData = {};
- // 数据做快照处理
- // 1.地址快照
- const addressData = await this.addressModel.findById(address._id);
- if (!addressData) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到邮寄地址数据');
- // 2.商品快照
- const goodsData = [];
- // 商店不做快照,但是商品和商品对应的规格做快照
- const { populate } = this.ctx.service.shop.goodsSpec.getRefMods();
- for (const i of goods) {
- const { goods: goodsList, ...others } = i;
- const qp = [];
- for (const g of goodsList) {
- const { goodsSpec_id, num, cart_id } = g;
- let d = await this.goodsSpecModel.findById(goodsSpec_id).populate(populate);
- if (!d) continue;
- d = JSON.parse(JSON.stringify(d));
- // 将商店内容剔除
- const { goods: gd, ...dOthers } = d;
- const gdOthers = _.omit(gd, [ 'shop' ]);
- const obj = { ...dOthers, goods: gdOthers, buy_num: num };
- if (cart_id) obj.cart_id = cart_id;
- qp.push(obj);
- }
- goodsData.push({ ...others, goods: qp });
- }
- // TODO: 3.商品总计明细.这地方没加优惠券,所以先直接复制即可
- const totalDetailData = total_detail;
- // 接下来组织订单数据
- orderData.address = addressData;
- orderData.goods = goodsData;
- orderData.total_detail = totalDetailData;
- // 1.用户数据
- orderData.customer = customer;
- // 2.下单时间
- orderData.buy_time = moment().format('YYYY-MM-DD HH:mm:ss');
- // 3.订单号
- const str = this.ctx.service.util.trade.createNonceStr();
- orderData.no = `${moment().format('YYYYMMDDHHmmss')}-${str}`;
- // 4.状态
- orderData.status = '0';
- // 生成数据
- // const order = await this.model.create(orderData);
- const order_id = this.tran.insert('Order', orderData);
- // 处理库存问题
- // 处理库存,删除购物车
- await this.dealGoodsNum(goodsData);
- await this.tran.run();
- return order_id;
- } catch (error) {
- await this.tran.rollback();
- console.error(error);
- throw new BusinessError(ErrorCode.SERVICE_FAULT, '订单发生错误,下单失败');
- } finally {
- // 清空事务
- this.tran.clean();
- }
- }
- /**
- * 减库存,删除购物车
- * @param {Array} list 商品
- */
- async dealGoodsNum(list) {
- for (const i of list) {
- for (const g of i.goods) {
- const { _id, buy_num, cart_id } = g;
- const goodsSpec = await this.goodsSpecModel.findById(_id);
- const newNum = parseInt(goodsSpec.num - buy_num);
- this.tran.update('GoodsSpec', _id, { num: newNum });
- if (cart_id) this.tran.remove('Cart', cart_id);
- }
- }
- }
- /**
- * 进入下单页面
- * @param {Object} body 请求参数
- * @param body.key 缓存key
- */
- async toMakeOrder({ key }) {
- key = `${this.redisKey.orderKeyPrefix}${key}`;
- let data = await this.redis.get(key);
- if (!data) throw new BusinessError(ErrorCode.SERVICE_FAULT, '请求超时,请重新进入下单页');
- data = JSON.parse(data);
- let specsData = [];
- if (_.isArray(data)) {
- // 购物车来的: 1.循环校验 规格商品; 2.按店铺分组
- const { populate } = this.ctx.service.trade.cart.getRefMods();
- const carts = await this.cartModel.find({ _id: data }).populate(populate);
- for (const cart of carts) {
- const { result, msg } = await this.ctx.service.util.trade.checkCanBuy(cart, false);
- if (!result) throw new BusinessError(ErrorCode.DATA_INVALID, msg);
- }
- specsData = this.setCartsGoodsToPageData(carts);
- } else if (_.isObject(data)) {
- // 商品页单独买: 1.校验规格商品; 2:按店铺分组
- const { result, msg } = await this.ctx.service.util.trade.checkCanBuy(data, false);
- if (!result) throw new BusinessError(ErrorCode.DATA_INVALID, msg);
- const { populate } = this.ctx.service.shop.goodsSpec.getRefMods();
- const list = await this.goodsSpecModel.find({ _id: data.goodsSpec }).populate(populate);
- specsData = this.setGoodsToPageData(list, data.num);
- } else throw new BusinessError(ErrorCode.DATA_INVALID, '数据不正确,请重新下单');
- // 组装页面的数据
- const user = this.ctx.user;
- const customer = _.get(user, '_id');
- if (!customer) throw new BusinessError(ErrorCode.NOT_LOGIN, '未找到用户信息');
- const pageData = {};
- // 找到默认地址
- const address = await this.addressModel.findOne({ customer, is_default: '1' });
- pageData.address = address;
- // // 商品总价,各店铺的价格明细
- specsData = this.computedShopTotal(specsData);
- const shopTotalDetail = this.computedAllTotal(specsData);
- pageData.goodsData = specsData;
- pageData.orderTotal = shopTotalDetail;
- return pageData;
- }
- /**
- * 计算商店的结算
- * @param {Array} list 按店铺分组的商品列表
- */
- computedShopTotal(list) {
- for (const i of list) {
- i.goods_total = _.floor(
- i.goods.reduce((p, n) => p + (n.money || 0) * (n.num || 0), 0),
- 2
- );
- i.freight_total = _.floor(
- i.goods.reduce((p, n) => p + (n.freight || 0) * (n.num || 0), 0),
- 2
- );
- }
- return list;
- }
- /**
- * 计算订单的结算
- * @param {Array} list 按店铺分组的商品列表
- */
- computedAllTotal(list) {
- const obj = {
- goods_total: _.floor(
- list.reduce((p, n) => p + (n.goods_total || 0), 0),
- 2
- ),
- freight_total: _.floor(
- list.reduce((p, n) => p + (n.freight_total || 0), 0),
- 2
- ),
- };
- return obj;
- }
- /**
- * 单商品整理数据,剩下的可以简略
- * @param {Array} list 规格数组
- * @param num 购买数量
- */
- setGoodsToPageData(list, num) {
- // 按店铺分组,精简字段
- const arr = [];
- for (const i of list) {
- const obj = {};
- obj.shop_name = _.get(i.goods, 'shop.name');
- obj.shop = _.get(i.goods, 'shop._id');
- const goods = {};
- goods.goods_id = _.get(i.goods, '_id');
- goods.goods_name = _.get(i.goods, 'name');
- goods.goodsSpec_id = _.get(i, '_id');
- goods.goodsSpec_name = _.get(i, 'name');
- goods.freight = _.get(i, 'freight');
- goods.money = _.get(i, 'sell_money');
- goods.num = num;
- goods.file = _.get(i.goods, 'file');
- obj.goods = [ goods ];
- arr.push(obj);
- }
- return arr;
- }
- /**
- *购物车数据整理
- * @param {Array} list 规格数组
- */
- setCartsGoodsToPageData(list) {
- const arr = [];
- list = _.groupBy(list, 'shop._id');
- for (const key in list) {
- const data = list[key];
- const shopData = _.get(_.head(data), 'shop');
- const obj = {};
- obj.shop = _.get(shopData, '_id');
- obj.shop_name = _.get(shopData, 'name');
- const goodsList = [];
- for (const i of data) {
- const goods = {};
- goods.cart_id = _.get(i, '_id');
- goods.goods_id = _.get(i.goods, '_id');
- goods.goods_name = _.get(i.goods, 'name');
- goods.goodsSpec_id = _.get(i.goodsSpec, '_id');
- goods.goodsSpec_name = _.get(i.goodsSpec, 'name');
- goods.freight = _.get(i.goodsSpec, 'freight');
- goods.money = _.get(i.goodsSpec, 'sell_money');
- goods.num = _.get(i, 'num');
- goods.file = _.get(i.goods, 'file', []);
- goodsList.push(goods);
- }
- obj.goods = goodsList;
- arr.push(obj);
- }
- return arr;
- }
- }
- module.exports = OrderService;
|