'use strict'; /** * 标段项目节数据模型 * * @author CaiAoLin * @date 2017/12/1 * @version */ module.exports = app => { class TenderNode extends app.BaseService { /** * 构造函数 * * @param {Object} ctx - egg全局变量 * @return {void} */ constructor(ctx) { super(ctx); this.tableName = 'tender_node'; } /** * 新增数据 * * @param {Object} data - 新增的数据(可批量) * @param {Number} tenderId - 标段id * @return {Boolean} - 返回新增的结果 */ async add(data, tenderId) { this.transaction = await this.db.beginTransaction(); let result = false; try { if (tenderId <= 0) { throw '标段id错误'; } if (data instanceof Array) { // 数组则为批量插入 if (data.length <= 0) { throw '插入数据为空'; } // 整理数据 const insertData = []; for (const tmp of data) { tmp.template_id = tmp.id; tmp.template_pid = tmp.pid; tmp.tender_id = tenderId; delete tmp.id; delete tmp.pid; insertData.push(tmp); } const operate = await this.transaction.insert(this.tableName, insertData); this.transaction.commit(); result = operate.affectedRows > 0; } else { // 对象则单个插入 } } catch (error) { result = false; } return result; } /** * 根据层级获取数据 * * @param {Number} tenderId - 标段id * @param {Boolean} showAll - 是否显示全部 * @return {Array} - 返回数据 */ async getDataByTenderId(tenderId, showAll = false) { if (tenderId <= 0) { return []; } const showLevel = !showAll ? 2 : -1; this.initSqlBuilder(); this.sqlBuilder.setAndWhere('tender_id', { value: tenderId, operate: '=', }); if (showLevel > 0) { this.sqlBuilder.setAndWhere('level', { value: showLevel, operate: '<=', }); } const [sql, sqlParam] = this.sqlBuilder.build(this.tableName); const data = await this.db.query(sql, sqlParam); return data; } /** * 根据节点Id获取数据 * * @param {Number} tenderId - 标段id * @param {Number} nodeId - 项目节/工程量清单节点id * @return {Object} - 返回查询到的节点数据 */ async getDataByNodeId(tenderId, nodeId) { if ((nodeId <= 0) || (tenderId <= 0)) { return undefined; } this.initSqlBuilder(); this.sqlBuilder.setAndWhere('tender_id', { value: tenderId, operate: '=', }); this.sqlBuilder.setAndWhere('template_id', { value: nodeId, operate: '=', }); const [sql, sqlParam] = this.sqlBuilder.build(this.tableName); const data = await this.db.queryOne(sql, sqlParam); return data; } /** * 获取最末的子节点 * @param {Number} tenderId - 标段id * @param {Number} pid - 父节点id * @returns {Object} */ async getLastChildData(tenderId, pid) { this.initSqlBuilder(); this.sqlBuilder.setAndWhere('tender_id', { value: tenderId, operate: '=' }); this.sqlBuilder.setAndWhere('template_pid', { value: pid, operate: '=' }); this.sqlBuilder.orderBy = [['order', 'DESC']]; const [sql, sqlParam] = this.sqlBuilder.build(this.tableName); const resultData = this.db.queryOne(sql, sqlParam); return resultData; } /** * 根据 父节点id 和 节点排序order 获取数据 * * @param {Number} tenderId - 标段id * @param {Number} pid - 父节点id * @param {Number|Array} order - 排序 * @returns {Object|Array} - 查询结果 */ async getDataByParentAndOrder(tenderId, pid, order) { if ((tenderId <= 0) || (pid <= 0) || (order <= 0)) { return undefined; } this.initSqlBuilder(); this.sqlBuilder.setAndWhere('tender_id', { value: tenderId, operate: '=' }); this.sqlBuilder.setAndWhere('template_pid', { value: pid, operate: '=', }); if (order instanceof Array) { this.sqlBuilder.setAndWhere('order', { value: order, operate: 'in' }); } else { this.sqlBuilder.setAndWhere('order', { value: order, operate: '=' }); } const [sql, sqlParam] = this.sqlBuilder.build(this.tableName); let data; if (order instanceof Array) { data = await this.db.query(sql, sqlParam); } else { data = await this.db.queryOne(sql, sqlParam); } return data; } /** * 根据 父节点ID 和 节点排序order 获取全部后节点数据 * @param {Number} tenderId - 标段id * @param {Number} pid - 父节点id * @param {Number} order - 排序 * @returns {Array} */ async getNextsData(tenderId, pid, order) { if ((tenderId <= 0) || (pid <= 0) || (order <= 0)) { return undefined; } this.initSqlBuilder(); this.sqlBuilder.setAndWhere('tender_id', { value: tenderId, operate: '=' }); this.sqlBuilder.setAndWhere('template_pid', { value: pid, operate: '=', }); this.sqlBuilder.setAndWhere('order', { value: order, operate: '>' }); const [sql, sqlParam] = this.sqlBuilder.build(this.tableName); const data = await this.db.query(sql, sqlParam); return data; } /** * select的全部后兄弟节点,Order自增 * * @param {Object} select - 选中的节点 * @param {Number} incre - 自增值 * @return {Array} - 自增后的数据 * @private */ async _updateSelectNextsOrder(select, incre = 1) { this.initSqlBuilder(); this.sqlBuilder.setAndWhere('tender_id', { value: select.tender_id, operate: '=', }); this.sqlBuilder.setAndWhere('order', { value: select.order + 1, operate: '>=', }); this.sqlBuilder.setAndWhere('template_pid', { value: select.template_pid, operate: '=', }); this.sqlBuilder.setUpdateData('order', { value: Math.abs(incre), selfOperate: incre > 0 ? '+' : '-', }); // sql = update this.tableName set order = order + 1 where (tender_id = select.tender_id) && (pid = select.pid) && (order >= select.order+1) const [sql, sqlParam] = this.sqlBuilder.build(this.tableName, 'update'); const data = await this.transaction.query(sql, sqlParam); return data; } /** * 从数据库获取标段的最大节点id * * @param {Number} tenderId - 标段id * @return {Number} * @private */ async _getMaxNodeId(tenderId) { const sql = 'SELECT Max(??) As max_id FROM ?? Where tender_id = ' + tenderId; const sqlParam = ['template_id', this.tableName]; const queryResult = await this.db.queryOne(sql, sqlParam); return queryResult.max_id; } /** * 根据selectData, data 新增数据 * * @param {Number} tenderId - 标段id * @param {Object} selectData - 选中节点的数据 * @param {Object} data - 新增节点的初始数据 * @return {Object} - 新增结果 * @private */ async _addNodeData(tenderId, selectData, data) { if (tenderId <= 0) { return undefined; } if (!data) { data = {}; } const cacheKey = 'tender_node_maxId:' + tenderId; let maxId = parseInt(await this.cache.get(cacheKey)); if (!maxId) { maxId = await this._getMaxNodeId(tenderId); this.cache.set(cacheKey, maxId, 'EX', this.ctx.app.config.cacheTime); } data.tender_id = tenderId; data.template_id = maxId + 1; data.template_pid = selectData.template_pid; data.level = selectData.level; data.order = selectData.order + 1; data.full_path = selectData.full_path.replace(selectData.template_id, data.template_id); const result = await this.transaction.insert(this.tableName, data); this.cache.set(cacheKey, maxId + 1, 'EX', this.ctx.app.config.cacheTime); return result; } /** * tenderId标段中, 在selectId后新增一个节点 * * @param {Number} tenderId - 标段id * @param {Number} selectId - 选中节点id * @param {Object} data - 新增节点初始化数据 * @return {Array} 新增后的数据,其他被修改的数据 */ async addNode(tenderId, selectId, data) { if ((tenderId <= 0) || (selectId <= 0)) { return []; } const selectData = await this.getDataByNodeId(tenderId, selectId); this.transaction = await this.db.beginTransaction(); if (selectData) { try { // 选中节点的所有后兄弟节点,order+1 await this._updateSelectNextsOrder(selectData); // 数据库创建新增节点数据 await this._addNodeData(tenderId, selectData, data); await this.transaction.commit(); } catch (err) { await this.transaction.rollback(); throw err; } // 查询应返回的结果 this.initSqlBuilder(); this.sqlBuilder.setAndWhere('tender_id', { value: selectData.tender_id, operate: '=', }); this.sqlBuilder.setAndWhere('template_pid', { value: selectData.template_pid, operate: '=', }); this.sqlBuilder.setAndWhere('order', { value: selectData.order, operate: '>', }); const [sql, sqlParam] = this.sqlBuilder.build(this.tableName); const resultData = this.db.query(sql, sqlParam); return resultData; } else { throw '新增节点数据错误'; } } /** * tenderId标段中, 删除选中节点及其子节点 * * @param {Number} tenderId - 标段id * @param {Number} selectId - 选中节点id * @return {Array} - 被删除的数据 */ async deleteNode(tenderId, selectId) { if ((tenderId <= 0) || (selectId <= 0)) { return []; } const selectData = await this.getDataByNodeId(tenderId, selectId); this.transaction = await this.db.beginTransaction(); let resultData = []; if (selectData) { try { // 获取将要被删除的数据 this.initSqlBuilder(); this.sqlBuilder.setAndWhere('tender_id', { value: tenderId, operate: '=' }); this.sqlBuilder.setAndWhere('full_path', { value: this.db.escape(selectData.full_path + '%'), operate: 'Like' }); let [sql, sqlParam] = this.sqlBuilder.build(this.tableName); resultData = await this.db.query(sql, sqlParam); // 删除 this.initSqlBuilder(); this.sqlBuilder.setAndWhere('tender_id', { value: tenderId, operate: '=' }); this.sqlBuilder.setAndWhere('full_path', { value: this.db.escape(selectData.full_path + '%'), operate: 'Like' }); [sql, sqlParam] = this.sqlBuilder.build(this.tableName, 'delete'); const operate = await this.transaction.query(sql, sqlParam); this.transaction.commit(); } catch(err) { resultData = []; await this.transaction.rollback(); throw err; } return resultData; } else { throw '删除节点数据错误'; } } /** * tenderId标段中, 选中节点selectId上移 * * @param {Number} tenderId - 标段id * @param {Number} selectId - 选中节点id * @return {Array} - 发生改变的数据 */ async upMoveNode(tenderId, selectId) { if ((tenderId <= 0) || (selectId <= 0)) { return []; } const selectData = await this.getDataByNodeId(tenderId, selectId); const preData = await this.getDataByParentAndOrder(tenderId, selectData.template_pid, selectData.order - 1); this.transaction = await this.db.beginTransaction(); if (selectData && preData) { try { const sData = await this.transaction.update(this.tableName, {id: selectData.id, order: selectData.order - 1}); const pData = await this.transaction.update(this.tableName, {id: preData.id, order: preData.order + 1}); this.transaction.commit(); } catch (err) { await this.transaction.rollback(); throw err; } const resultData = await this.getDataByParentAndOrder(tenderId, selectData.template_pid, [selectData.order, preData.order]); return resultData; } else { throw '上移节点数据错误'; } } /** * tenderId标段中, 选中节点selectId下移 * * @param {Number} tenderId - 标段id * @param {Number} selectId - 选中节点id * @return {Array} - 发生改变的数据 */ async downMoveNode(tenderId, selectId) { if ((tenderId <= 0) || (selectId <= 0)) { return []; } const selectData = await this.getDataByNodeId(tenderId, selectId); const nextData = await this.getDataByParentAndOrder(tenderId, selectData.template_pid, selectData.order + 1); this.transaction = await this.db.beginTransaction(); if (selectData && nextData) { try { const sData = await this.transaction.update(this.tableName, {id: selectData.id, order: selectData.order + 1}); const pData = await this.transaction.update(this.tableName, {id: nextData.id, order: nextData.order - 1}); this.transaction.commit(); } catch (err) { await this.transaction.rollback(); throw err; } const resultData = await this.getDataByParentAndOrder(tenderId, selectData.template_pid, [selectData.order, nextData.order]); return resultData; } else { throw '下移节点数据错误'; } } /** * 升级selectData, 同步修改所有子节点 * @param {Object} selectData - 升级操作,选中节点 * @return {Object} * @private */ async _syncUplevelChildren(selectData) { this.initSqlBuilder(); this.sqlBuilder.setAndWhere('tender_id', { value: selectData.tender_id, operate: '=' }); this.sqlBuilder.setAndWhere('full_path', { value: this.db.escape(selectData.full_path + '.%'), operate: 'like' }); this.sqlBuilder.setUpdateData('level', { value: 1, selfOperate: '-' }); this.sqlBuilder.setUpdateData('full_path', { value: ['`full_path`', this.db.escape(selectData.template_pid + '.'), this.db.escape('')], literal: 'Replace' }); const [sql, sqlParam] = this.sqlBuilder.build(this.tableName, 'update'); const data = this.transaction.query(sql, sqlParam); return data; } /** * 选中节点的后兄弟节点,全部变为当前节点的子节点 * @param {Object} selectData - 选中节点 * @return {Object} * @private */ async _syncUpLevelNexts(selectData) { // 查询selectData的lastChild const lastChildData = await this.getLastChildData(selectData.tender_id, selectData.template_id); const nextsData = await this.getNextsData(selectData.tender_id, selectData.template_pid, selectData.order); if (nextsData && nextsData.length > 0) { // 修改nextsData pid, 排序 this.initSqlBuilder(); this.sqlBuilder.setUpdateData('template_pid', { value: selectData.template_id }); const orderInc = lastChildData ? lastChildData.order - selectData.order : - selectData.order; this.sqlBuilder.setUpdateData('order', { value: Math.abs(orderInc), selfOperate: orderInc > 0 ? '+' : '-' }); this.sqlBuilder.setAndWhere('template_pid', { value: selectData.template_pid, operate: '=' }); this.sqlBuilder.setAndWhere('order', { value: selectData.order, operate: '>' }); const [sql1, sqlParam1] = this.sqlBuilder.build(this.tableName, 'update'); await this.transaction.query(sql1, sqlParam1); // 修改nextsData及其子节点的full_path const oldSubStr = this.db.escape(selectData.template_pid + '.'); const newSubStr = this.db.escape(selectData.template_id + '.'); const sqlArr = []; sqlArr.push('Update ?? SET `full_path` = Replace(`full_path`,' + oldSubStr + ',' + newSubStr + ') Where'); sqlArr.push('(`tender_id` = ' + selectData.tender_id +')'); sqlArr.push(' And ('); for (const data of nextsData) { sqlArr.push('`full_path` Like ' + this.db.escape(data.full_path + '%')); if (nextsData.indexOf(data) < nextsData.length - 1) { sqlArr.push(' Or '); } } sqlArr.push(')'); const sql = sqlArr.join(''); const resultData = await this.transaction.query(sql, [this.tableName]); return resultData; } } /** * 升级节点 * * @param {Number} tenderId - 标段id * @param {Number} selectId - 选中节点id * @return {Array} - 发生改变的数据 */ async upLevelNode(tenderId, selectId) { if ((tenderId <= 0) || (selectId <= 0)) { return []; } const selectData = await this.getDataByNodeId(tenderId, selectId); if (!selectData) { throw '升级节点数据错误'; } const parentData = await this.getDataByNodeId(tenderId, selectData.template_pid); if (!parentData) { throw '升级节点数据错误' } this.transaction = await this.db.beginTransaction(); try { // 选中节点--父节点--全部后兄弟节点 order+1 await this._updateSelectNextsOrder(parentData); // 选中节点 修改pid, order, full_path const updateData = {id: selectData.id, template_pid: parentData.template_pid, order: parentData.order + 1, level: selectData.level - 1, full_path: selectData.full_path.replace(selectData.template_pid + '.', '') }; await this.transaction.update(this.tableName, updateData); // 选中节点--全部子节点(含孙) level-1, full_path变更 await this._syncUplevelChildren(selectData); // 选中节点--全部后兄弟节点 收编为子节点 修改pid, order, full_path await this._syncUpLevelNexts(selectData); this.transaction.commit(); } catch (err) { await this.transaction.rollback(); throw err; } // 查询修改的数据 this.initSqlBuilder(); this.sqlBuilder.setAndWhere('tender_id', { value: tenderId, operate: '=' }); this.sqlBuilder.setAndWhere('full_path', { value: this.db.escape(selectData.full_path.replace(selectData.template_pid + '.', '') + '%'), operate: 'Like' }); const [sql, sqlParam] = this.sqlBuilder.build(this.tableName); const resultData1 = await this.db.query(sql, sqlParam); const resultData2 = await this.getNextsData(tenderId, parentData.template_pid, parentData.order + 1); return resultData1.concat(resultData2); } /** * 降级selectData, 同步修改所有子节点 * @param {Object} selectData - 选中节点 * @param {Object} preData - 选中节点的前一节点(降级后为父节点) * @returns {Promise<*>} * @private */ async _syncDownlevelChildren(selectData, preData) { this.initSqlBuilder(); this.sqlBuilder.setAndWhere('tender_id', { value: selectData.tender_id, operate: '=' }); this.sqlBuilder.setAndWhere('full_path', { value: this.db.escape(selectData.full_path + '.%'), operate: 'like' }); this.sqlBuilder.setUpdateData('level', { value: 1, selfOperate: '+' }); this.sqlBuilder.setUpdateData('full_path', { value: ['`full_path`', this.db.escape(selectData.template_id), this.db.escape(preData.template_id + '.' + selectData.template_id)], literal: 'Replace' }); const [sql, sqlParam] = this.sqlBuilder.build(this.tableName, 'update'); const data = this.transaction.query(sql, sqlParam); return data; } /** * 降级节点 * * @param {Number} tenderId - 标段id * @param {Number} selectId - 选中节点id * @return {Array} - 发生改变的数据 */ async downLevelNode(tenderId, selectId) { if ((tenderId <= 0) || (selectId <= 0)) { return []; } const selectData = await this.getDataByNodeId(tenderId, selectId); if (!selectData) { throw '降级节点数据错误'; } const preData = await this.getDataByParentAndOrder(tenderId, selectData.template_pid, selectData.order-1); if (!preData) { throw '节点不可降级'; } const preLastChildData = await this.getLastChildData(tenderId, preData.template_id); this.transaction = await this.db.beginTransaction(); try { // 选中节点--全部后节点 order-- await this._updateSelectNextsOrder(selectData, -1); // 选中节点 修改pid, level, order, full_path const updateData = {id: selectData.id, template_pid: preData.template_pid, order: preLastChildData ? preLastChildData.order + 1 : 1, level: selectData.level + 1, full_path: selectData.full_path.replace(selectData.template_id, preData.template_id + '.' + selectData.template_id) }; await this.transaction.update(this.tableName, updateData); // 选中节点--全部子节点(含孙) level++, full_path await this._syncDownlevelChildren(selectData, preData); this.transaction.commit(); } catch (err) { this.transaction.rollback(); throw err; } // 查询修改的数据 this.initSqlBuilder(); this.sqlBuilder.setAndWhere('tender_id', { value: tenderId, operate: '=' }); this.sqlBuilder.setAndWhere('full_path', { value: this.db.escape(selectData.full_path.replace(selectData.template_id, preData.template_id + '.' + selectData.template_id) + '%'), operate: 'Like' }); const [sql, sqlParam] = this.sqlBuilder.build(this.tableName); const resultData1 = await this.db.query(sql, sqlParam); const resultData2 = await this.getNextsData(tenderId, preData.template_pid, preData.order); return resultData1.concat(resultData2); } } return TenderNode; };