123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199 |
- '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');
- const { ObjectId } = require('mongoose').Types;
- //
- class ZrOrderService extends CrudService {
- constructor(ctx) {
- super(ctx, 'zrorder');
- this.redis = this.app.redis;
- this.redisKey = this.app.config.redisKey;
- this.redisTimeout = this.app.config.redisTimeout;
- this.model = this.ctx.model.ZrOrder;
- this.goodsModel = this.ctx.model.ZrGoods;
- this.pointModel = this.ctx.model.Base.Point;
- this.shopModel = this.ctx.model.Base.Shop;
- this.addressModel = this.ctx.model.Base.Address;
- this.tran = new Transaction();
- }
- // 进入订单页前,通过函数判断,存储是否可以购买 指定商品 的 指定数量
- // 下单,走同样的函数判断.
- // 直接生成订单,扣除积分
- async fetch(filter, { sort, desc, projection } = {}) {
- assert(filter);
- filter = await this.beforeFetch(filter);
- const { _id, id } = filter;
- if (_id || id) filter = { _id: ObjectId(_id || id) };
- // 处理排序
- if (sort && _.isString(sort)) {
- sort = { [sort]: desc ? -1 : 1 };
- } else if (sort && _.isArray(sort)) {
- sort = sort.map(f => ({ [f]: desc ? -1 : 1 })).reduce((p, c) => ({ ...p, ...c }), {});
- }
- const { populate } = this.getRefMods();
- let res = await this.model.findOne(filter, projection).populate(populate).exec();
- res = await this.afterFetch(filter, res);
- return res;
- }
- async create(data) {
- const { shop, goods, buy_num, address, remarks } = data;
- const res = await this.checkCanBuy({ shop, goods, num: buy_num }, false);
- if (!res.result) throw new BusinessError(ErrorCode.DATA_INVALID, res.msg);
- // 可以购买
- const customer = _.get(this.ctx.user, '_id');
- const goodsInfo = await this.goodsModel.findById(goods);
- const buy_time = moment().format('YYYY-MM-DD HH:mm:ss');
- const no = `${moment().format('YYYYMMDDHHmmss')}-${this.createNonceStr()}`;
- const addressInfo = await this.addressModel.findById(address);
- try {
- const orderData = {
- customer,
- shop,
- goods: goodsInfo,
- buy_num,
- buy_time,
- no,
- address: addressInfo,
- status: '1',
- remarks,
- };
- const order_id = this.tran.insert('ZrOrder', orderData);
- // #region 积分处理
- const costTotal = this.ctx.multiply(_.get(goodsInfo, 'cost'), buy_num);
- const pointData = { customer, point: costTotal, time: buy_time, source: '-2', source_id: order_id };
- this.tran.insert('Point', pointData);
- // #endregion
- // #region 库存处理
- const newNum = this.ctx.minus(_.get(goodsInfo, 'num'), buy_num);
- this.tran.update('ZrGoods', goods, { num: newNum });
- // #endregion
- await this.tran.run();
- return 'ok';
- } catch (error) {
- await this.tran.rollback();
- console.error(error);
- } finally {
- this.tran.clean();
- }
- }
- 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);
- const { shop, goods, num: buy_num } = data;
- // 店铺信息
- const shopInfo = await this.shopModel.findById(shop);
- // 商品信息
- const goodsInfo = await this.goodsModel.findById(goods);
- // 消耗积分
- const costTotal = this.ctx.multiply(_.get(goodsInfo, 'cost'), buy_num);
- // 积分信息
- const customer = _.get(this.ctx.user, '_id');
- const points = await this.pointComputedTotal({ customer });
- // 地址信息
- const address = await this.addressModel.findOne({ customer, is_default: '1' });
- return { buy_num, shop: shopInfo, goods: goodsInfo, points, address, costTotal };
- }
- /**
- * 检查是否可以购买商品,并生成缓存
- * @param {Object} body 参数体
- * @param body.shop 店铺id
- * @param body.goods 尊荣商品id
- * @param body.num 购买数量
- * @param {Boolean} makeCache 是否生成缓存
- */
- async checkCanBuy({ shop, goods, num }, makeCache = true) {
- const result = { result: true };
- const shopInfo = await this.shopModel.findById(shop);
- if (!shopInfo || _.get(shopInfo, '1')) {
- result.result = false;
- result.msg = '店铺当前不处于营业状态';
- return result;
- }
- const goodsInfo = await this.goodsModel.findById(goods);
- if (!goodsInfo) {
- result.result = false;
- result.msg = '未找到尊荣商品';
- return result;
- }
- const user = this.ctx.user;
- const customer = _.get(user, '_id');
- if (!customer) {
- result.result = false;
- result.msg = '未找到用户信息';
- }
- // #region 库存判断
- const knum = _.get(goodsInfo, 'num');
- const res = this.ctx.minus(knum, num);
- if (res <= 0) {
- result.result = false;
- result.msg = '库存不足';
- return result;
- }
- // #endregion
- // #region 积分判断
- const pointTotal = await this.pointComputedTotal({ customer });
- // 计算购买所需积分
- const cost = _.get(goodsInfo, 'cost', 0);
- if (cost === 0) {
- result.result = false;
- result.msg = '商品设置的尊荣有误,无法进行购买';
- return result;
- }
- const costTotal = this.ctx.multiply(cost, num);
- const afterCost = this.ctx.minus(pointTotal, costTotal);
- if (afterCost < 0) {
- result.result = false;
- result.msg = '您的积分不足';
- return result;
- }
- // #endregion
- if (makeCache) {
- const key = await this.makeOrderKey({ shop, goods, num });
- result.key = key;
- }
- return result;
- }
- // 生成key
- async makeOrderKey(data) {
- const str = this.createNonceStr();
- const key = `${this.redisKey.orderKeyPrefix}${str}`;
- const value = JSON.stringify(data);
- await this.redis.set(key, value, 'EX', this.redisTimeout);
- return str;
- }
- /**
- * 计算用户的积分
- * @param {Object} data 数据对象
- * @param data.customer 用户数据id
- */
- async pointComputedTotal({ customer }) {
- assert(customer, '缺少用户信息');
- const res = await this.pointModel.find({ customer });
- const total = res.reduce((p, n) => {
- let point = n.point;
- if (!(n.source === '0' || n.source === '1')) point = -point;
- return this.ctx.plus(p, point);
- }, 0);
- return total;
- }
- // 随机字符串产生函数
- createNonceStr() {
- return Math.random().toString(36).substr(2, 15);
- }
- }
- module.exports = ZrOrderService;
|