Bläddra i källkod

1. sql_builder增加支持mySql内置函数
2. 台账,升级、降级节点

MaiXinRong 7 år sedan
förälder
incheckning
1d22a45910

+ 12 - 6
app/lib/sql_builder.js

@@ -101,14 +101,20 @@ class SqlBuilder {
         }
         const setDataArr = [];
         for (const set of this.setData) {
-            const tmp = set.data.selfOperate !== undefined ?
-                ' ?? = ?? ' + set.data.selfOperate + ' ' + set.data.value : ' ?? = ' + set.data.value;
-            setDataArr.push(tmp);
-            // 如果是自身操作则压多一次字段进数组
-            if (set.data.selfOperate !== undefined) {
+            if (set.data.literal) {
+                const values = set.data.value instanceof Array ? set.data.value : [set.data.value];
+                setDataArr.push(' ?? = ' + set.data.literal + '(' + values.join(',') + ')');
+                this.sqlParam.push(set.field);
+            } else {
+                const tmp = set.data.selfOperate !== undefined ?
+                    ' ?? = ?? ' + set.data.selfOperate + ' ' + set.data.value : ' ?? = ' + set.data.value;
+                setDataArr.push(tmp);
+                // 如果是自身操作则压多一次字段进数组
+                if (set.data.selfOperate !== undefined) {
+                    this.sqlParam.push(set.field);
+                }
                 this.sqlParam.push(set.field);
             }
-            this.sqlParam.push(set.field);
         }
 
         const setString = setDataArr.join(',');

+ 294 - 3
app/service/tender_node.js

@@ -125,7 +125,31 @@ module.exports = app => {
         }
 
         /**
+         * 获取最末的子节点
+         * @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 - 排序
@@ -169,13 +193,46 @@ module.exports = app => {
         }
 
         /**
+         * 根据 父节点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) {
+        async _updateSelectNextsOrder(select, incre = 1) {
             this.initSqlBuilder();
             this.sqlBuilder.setAndWhere('tender_id', {
                 value: select.tender_id,
@@ -190,8 +247,8 @@ module.exports = app => {
                 operate: '=',
             });
             this.sqlBuilder.setUpdateData('order', {
-                value: 1,
-                selfOperate: '+',
+                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');
@@ -202,6 +259,7 @@ module.exports = app => {
 
         /**
          * 从数据库获取标段的最大节点id
+         *
          * @param {Number} tenderId - 标段id
          * @return {Number}
          * @private
@@ -215,6 +273,7 @@ module.exports = app => {
 
         /**
          * 根据selectData, data 新增数据
+         *
          * @param {Number} tenderId - 标段id
          * @param {Object} selectData - 选中节点的数据
          * @param {Object} data - 新增节点的初始数据
@@ -250,6 +309,7 @@ module.exports = app => {
 
         /**
          * tenderId标段中, 在selectId后新增一个节点
+         *
          * @param {Number} tenderId - 标段id
          * @param {Number} selectId - 选中节点id
          * @param {Object} data - 新增节点初始化数据
@@ -300,6 +360,7 @@ module.exports = app => {
 
         /**
          *  tenderId标段中, 删除选中节点及其子节点
+         *
          * @param {Number} tenderId - 标段id
          * @param {Number} selectId - 选中节点id
          * @return {Array} - 被删除的数据
@@ -352,6 +413,7 @@ module.exports = app => {
 
         /**
          * tenderId标段中, 选中节点selectId上移
+         *
          * @param {Number} tenderId - 标段id
          * @param {Number} selectId - 选中节点id
          * @return {Array} - 发生改变的数据
@@ -383,6 +445,7 @@ module.exports = app => {
 
         /**
          * tenderId标段中, 选中节点selectId下移
+         *
          * @param {Number} tenderId - 标段id
          * @param {Number} selectId - 选中节点id
          * @return {Array} - 发生改变的数据
@@ -411,6 +474,234 @@ module.exports = app => {
                 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;

+ 5 - 1
test/app/lib/sql_builder.test.js

@@ -67,6 +67,10 @@ describe('test/app/lib/sql_builder.test.js', () => {
         sqlBuilder.setUpdateData('office', {
             value: 2,
         });
+        sqlBuilder.setUpdateData('full_path', {
+            value: ['`full_path`', app.mysql.escape('1.'), app.mysql.escape('2.')],
+            literal: 'Replace'
+        });
         sqlBuilder.setAndWhere('group_id', {
             value: 1,
             operate: '>=',
@@ -74,7 +78,7 @@ describe('test/app/lib/sql_builder.test.js', () => {
         const [sql, sqlParam] = sqlBuilder.build('table', 'update');
         const finalSql = app.mysql.format(sql, sqlParam);
 
-        const matchSql = 'UPDATE `table` SET  `create_time` = `create_time` + 1, `office` = 2 WHERE  `group_id` >= 1';
+        const matchSql = "UPDATE `table` SET  `create_time` = `create_time` + 1, `office` = 2, `full_path` = Replace(`full_path`,'1.','2.') WHERE  `group_id` >= 1";
         assert(finalSql === matchSql);
     });
 

+ 33 - 3
test/app/service/tender_node.test.js

@@ -37,10 +37,12 @@ const testNodeData = [
     { template_id: 13, template_pid: 2, order: 2, level: 3, full_path: '1.2.13', code: '1-1-2' },
     { template_id: 14, template_pid: 2, order: 3, level: 3, full_path: '1.2.14', code: '1-1-3' },
     { template_id: 3, template_pid: 1, order: 2, level: 2, full_path: '1.3', code: '1-2' },
+    { template_id: 15, template_pid: 3, order: 1, level: 3, full_path: '1.3.15', code: '1-2-1'},
     { template_id: 4, template_pid: 1, order: 3, level: 2, full_path: '1.4', code: '1-3' },
+    { template_id: 16, template_pid: 4, order: 1, level: 3, full_path: '1.4.16', code: '1-3-1'},
     { template_id: 5, template_pid: 1, order: 4, level: 2, full_path: '1.5', code: '1-4' },
 ];
-const testTenderId = 2;
+const testTenderId = 3;
 
 const { app, assert } = require('egg-mock/bootstrap');
 
@@ -57,7 +59,7 @@ describe('test/app/service/tender_node.test.js', () => {
             data.tender_id = testTenderId;
         }
         const result = yield ctx.service.tenderNode.db.insert(ctx.service.tenderNode.tableName, testNodeData);
-        assert(result.affectedRows === 14);
+        assert(result.affectedRows === testNodeData.length);
     });
 
     it('test addNode', function* () {
@@ -85,7 +87,7 @@ describe('test/app/service/tender_node.test.js', () => {
         assert(resultData[0].code === '202-2-e');
     });
 
-    it('test upMoveNode', function* () {
+    it('test downMoveNode', function* () {
         const ctx = app.mockContext();
         // 选中202-2-e下移
         let resultData = yield ctx.service.tenderNode.downMoveNode(testTenderId, 12);
@@ -95,4 +97,32 @@ describe('test/app/service/tender_node.test.js', () => {
         assert(resultData.length === 2);
         assert(resultData[0].code === '202-2-c');
     });
+
+    it('test upLevelNode', function* () {
+        const ctx = app.mockContext();
+        // 选中 1-1-2 升级
+        let resultData = yield ctx.service.tenderNode.upLevelNode(testTenderId, 13);
+        assert(resultData);
+        assert(resultData.length === 5);
+        assert(resultData[0].full_path === '1.13');
+        assert(resultData[0].template_pid === 1);
+        assert(resultData[1].template_pid === 13);
+        assert(resultData[1].full_path === '1.13.14');
+        assert(resultData[2].order === 3);
+        assert(resultData[3].order === 4);
+        assert(resultData[4].order === 5);
+    });
+
+    it('test downLevelNode', function* () {
+        const ctx = app.mockContext();
+        // 选中1-3 降级
+        let resultData = yield ctx.service.tenderNode.downLevelNode(testTenderId, 4);
+        assert(resultData.length === 3);
+        assert(resultData[0].full_path === '1.3.4');
+        assert(resultData[0].level === 3);
+        assert(resultData[0].order === 2);
+        assert(resultData[1].level === 4);
+        assert(resultData[1].full_path === '1.3.4.16');
+        assert(resultData[2].order === 4);
+    })
 });