'use strict'; const assert = require('assert'); const _ = require('lodash'); const { ObjectId } = require('mongoose').Types; const { CrudService } = require('naf-framework-mongoose/lib/service'); const { BusinessError, ErrorCode } = require('naf-core').Error; class OrderService extends CrudService { constructor(ctx) { super(ctx, 'order'); this.model = this.ctx.model.Order; } /** * 新添订单 * @param {Object} data 订单数据 */ async create(data) { const res = await this.model.create(data); // 复制进split中 if (!res) throw new BusinessError(ErrorCode.SERVICE_FAULT, '订单创建失败'); await this.copyToSplit(res._id); try { this.record(res._id, { method: 'create' }); } catch (error) { this.logger.error(`订单id:${res.id}记录创建失败:${error.toString()}`); } return res; } /** * 修改订单 * @param {Object} { id, ...data } 要修改的订单数据 */ async update({ id, ...data }) { const res = await this.model.findById(id); const { goods: newGoods, ...info } = data; let { goods: oldGoods, split } = res; if (oldGoods) oldGoods = JSON.parse(JSON.stringify(oldGoods)); if (split) split = JSON.parse(JSON.stringify(split)); // 找到删除项 // oldGoods中有,但是newGoods中却没有的数据,就是要删除的;且需要检验被删除的数据有没有被拆分 oldGoods = oldGoods.map(og => { const need_delete = newGoods.find(f => ObjectId(f._id).equals(og._id)); if (!need_delete) { const sobj = split.find(f => ObjectId(f.pid).equals(og._id)); if (sobj) { // 查找其有没有被拆分 const r = split.find(f => ObjectId(f.pid).equals(sobj._id)); if (r) throw new BusinessError(ErrorCode.DATA_INVALID, '无法删除已被拆分过的货物'); const s_index = split.findIndex(f => ObjectId(f.pid).equals(og._id)); split.splice(s_index, 1); } return undefined; } return og; }); oldGoods = _.compact(oldGoods); // 判断是否有修改项 const updateRes = await this.goodsUpdate(oldGoods, newGoods, split); if (updateRes) { const { oldGoods: ogs, split: splist } = updateRes; if (ogs) oldGoods = ogs; if (splist) split = splist; } // 判断有没有新添的数据 const addGoods = newGoods.filter(f => !f._id); if (addGoods.length > 0) { // 有新增货物项 // TODO,复制到split中和oldGoods中 for (const ng of addGoods) { oldGoods.push(ng); } } res.goods = oldGoods; res.split = split; // console.group('oldGoods'); // console.log(oldGoods); // console.groupEnd(); // console.group('split'); // console.log(split); // console.groupEnd(); await this.model.update({ _id: ObjectId(id) }, info); await res.save(); this.copyToSplit(id); try { this.record(res._id, { method: 'update' }); } catch (error) { this.logger.error(`订单id:${res.id}记录创建失败:${error.toString()}`); } return; } /** * 检查并处理有修改的货物:拆分就不允许修改了 * @param {Array} oldGoods 查出修改前的货物列表 * @param {Array} newGoods 修改后的货物列表 * @param {Array} split 原拆分货物列表,用来检查是否可以修改 */ async goodsUpdate(oldGoods, newGoods, split) { oldGoods = oldGoods.map(og => { const is_split = split.find(f => ObjectId(f.pid).equals(og._id) && f.type === '1'); if (is_split) return og; // 没有拆分,可以直接更换 const ng = newGoods.find(f => ObjectId(f._id).equals(og._id)); if (ng) return { ...ng, update: true }; return og; }); // 修改拆分货物列表 const toUpdateSplit = oldGoods.filter(f => f.update); split = split.map(i => { const res = toUpdateSplit.find(f => ObjectId(f._id).equals(i.pid)); if (res) { const obj = this.goodsCopy(res); return obj; } return i; }); // 将oldGoods的update摘出去 oldGoods = oldGoods.map(i => _.omit(i, [ 'update' ])); return { oldGoods, split }; } /** * 将货物复制成拆分货物的数据并返回 * @param {Object} data 货物列表的每项 * @return split */ goodsCopy(data) { const split = _.pick(data, [ 'name', 'number', 'weight', 'volume', 'transport_type', 'remark', ]); split.pid = data._id; return split; } /** * 根据订单id,复制货物 到 拆分货物,只是复制,其他操作在各自的方法中,这里只是复制 * @param {String} id 订单id */ async copyToSplit(id) { const order = await this.model.findById(id); if (!order) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到订单'); const { goods, split } = order; for (const g of goods) { const has = split.find(f => ObjectId(f.pid).equals(g._id)); if (!has) { order.split.push(this.goodsCopy(g)); } } await order.save(); } /** * 发货,添加记录及修改状态 * @param {Array} goods 发货的货物列表 * @param {String} no 运输单号 */ async sendGoods(goods, no) { for (const g of goods) { const { split_id, name, number, weight, volume } = g; const order = await this.model.findOne({ 'split._id': ObjectId(split_id) }); if (!order) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, `未找到该${name}所在的订单信息`); const obj = { split_id, name, number, weight, volume, no }; // 添加该货物的发货记录 order.send_time.push(obj); // 修改该货物的状态 const good = order.split.id(split_id); good.status = '1'; order.goods_status = await this.checkGoodsStatus(order.split); await order.save(); // 更新记录 let message = `${name}已发货(数量:${number};重量:${weight}吨;体积:${volume}m³)`; const all_send = order.split.every(e => e.status === '0'); if (all_send) message = `${message}; 所有货物已发出`; try { this.record(order._id, { method: 'send', message }); } catch (error) { this.logger.error(`订单id:${order.id}记录创建失败:${error.toString()}`); } } } /** * 签收,添加记录及修改状态 * @param {Array} goods 签收的货物列表 * @param {String} no 运输单号 * @param {String} time 签收时间 */ async arriveGoods(goods, no, time) { for (const g of goods) { const { split_id, name, number, weight, volume } = g; const order = await this.model.findOne({ 'split._id': ObjectId(split_id) }); if (!order) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, `未找到该${name}所在的订单信息`); const obj = { split_id, name, number, weight, volume, no }; if (time) obj.time = time; // 添加收货记录 order.arrive_time.push(obj); // 修改该货物的状态 const good = order.split.id(split_id); good.status = '-1'; order.goods_status = await this.checkGoodsStatus(order.split); await order.save(); // 更新记录 let message = `${name}已签收(数量:${number};重量:${weight}吨;体积:${volume}m³)`; const all_arrive = order.split.every(e => e.status === '-1'); if (all_arrive) message = `${message}; 所有货物已签收`; try { this.record(order._id, { method: 'arrive', message }); } catch (error) { this.logger.error(`订单id:${order.id}记录创建失败:${error.toString()}`); } } } /** * 检查拆分货物列表,更新goods_status * @param {Array} splitList 拆分货物列表 */ async checkGoodsStatus(splitList) { // 未发车 const res = splitList.every(e => e.status === '0'); if (res) return '未发货'; // 检查是否全发车的货物 const all_send = splitList.every(e => e.status === '1'); if (all_send) return '所有货物已发出'; // 检查是否全到达了 const all_arrive = splitList.every(e => e.status === '-1'); if (all_arrive) return '所有货物全部到达'; // 检查是否有发货的 const is_send = splitList.some(e => e.status === '1'); // 检查是否有到达的 const is_arrive = splitList.some(e => e.status === '-1'); const word = []; if (is_send)word.push('部分货物已发出'); if (is_arrive)word.push('部分货物已到达'); if (word.length > 0) return word.join(';'); return '状态错误'; } /** * 订单操作记录 * @param {String} id 订单id * @param {Object} {method:方法, message:自定义文字} 参数 */ async record(id, { method, message }) { const order = await this.model.findById(id); if (!order) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到订单'); const { authorization } = this.ctx.request.header; let user = decodeURI(authorization); if (!user) { throw new BusinessError(ErrorCode.USER_NOT_EXIST, '未找到操作人'); } user = JSON.parse(user); const { id: userid, name: username } = user; const record = { opera: username, operaid: userid }; // 创建记录 if (method === 'create') record.message = `${username}创建订单`; else if (method === 'update') record.message = `${username}修改订单`; else if (method === 'in') record.message = `${username}修改收入`; else if (method === 'out') record.message = `${username}修改支出`; else if (method === 'split') record.message = `${username}拆分货物`; else if (method === 'send' || method === 'arrive') record.message = `${message}`; order.record.push(record); await order.save(); } } module.exports = OrderService;