| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051 | 'use strict';/** * 提供基础操作:增删改查 * * @author Mai * @date * @version */const Service = require('./base_service');// sql拼装器const SqlBuilder = require('../lib/sql_builder');const rootId = -1;class TreeService extends Service {    /**     * 构造函数     *     * @param {Object} ctx - egg全局context     * @param {Object} setting - 树结构设置     * e.g.: {     *     mid: 'tender_id', 分块id(例如tender_id, rid, list_id)     *     kid: 'ledger_id', 分块内的树结构id     *     pid: 'ledger_pid', 父节点id     *     order: 'order',     *     level: 'level',     *     fullPath: 'full_path',     *     isLeaf: 'is_leaf',     *     keyPre: 'revise_bills_maxLid:'     * }     * @return {void}     */    constructor(ctx, setting) {        super(ctx);        this.tableName = setting.tableName;        this.setting = setting;        // 以下字段仅可通过树结构操作改变,不可直接通过update方式从接口提交,发现时过滤        this.readOnlyFields = ['id'];        this.readOnlyFields.push(this.setting.mid);        this.readOnlyFields.push(this.setting.kid);        this.readOnlyFields.push(this.setting.pid);        this.readOnlyFields.push(this.setting.order);        this.readOnlyFields.push(this.setting.level);        this.readOnlyFields.push(this.setting.fullPath);        this.readOnlyFields.push(this.setting.isLeaf);    }    get rootId() {        return rootId;    }    getCondition (condition) {        const result = {};        if (condition.mid) result[this.setting.mid] = condition.mid;        if (condition.kid) result[this.setting.kid] = condition.kid;        if (condition.pid) result[this.setting.pid] = condition.pid;        if (condition.order) result[this.setting.order] = condition.order;        if (condition.level) result[this.setting.level] = condition.level;        if (condition.fullPath) result[this.setting.fullPath] = condition.fullPath;        if (condition.isLeaf) result[this.setting.isLeaf] = condition.isLeaf;        return result;    }    /**     * 获取 修订 清单数据     * @param {Number} mid - masterId     * @returns {Promise<void>}     */    async getData(mid, level) {        if (level) {            this.initSqlBuilder();            this.sqlBuilder.setAndWhere(this.setting.mid, {                operate: '=',                value: mid,            });            this.sqlBuilder.setAndWhere(this.setting.level, {                operate: '<=',                value: level,            });            const [sql, sqlParam] = this.sqlBuilder.build(this.departTableName(mid));            return await this.db.query(sql, sqlParam);        } else {            return await this.db.select(this.departTableName(mid), {                where: this.getCondition({ mid: mid })            });        }    }    /**     * 获取节点数据     * @param {Number} mid - masterId     * @param {Number} id     * @returns {Promise<void>}     */    async getDataByKid (mid, kid) {        return await this.db.get(this.tableName, this.getCondition({            mid: mid, kid: kid,        }));    }    async getDataByKidAndCount(mid, kid, count) {        if ((mid <= 0) || (kid <= 0)) return [];        const select = await this.getDataByKid(mid, kid);        if (!select) throw '数据错误';        if (count > 1) {            const selects = await this.getNextsData(mid, select[this.setting.pid], select[this.setting.order] - 1);            if (selects.length < count) throw '数据错误';            return selects.slice(0, count);        } else {            return [select];        }    }    /**     * 获取节点数据     * @param id     * @returns {Promise<Array>}     */    async getDataById(id) {        if (id instanceof Array) {            return await this.db.select(this.tableName, { where: {id: id} });        } else {            return await this.db.get(this.tableName, { id: id });        }    }    /**     * 获取最末的子节点     * @param {Number} mid - masterId     * @param {Number} pid - 父节点id     * @return {Object}     */    async getLastChildData(mid, pid) {        this.initSqlBuilder();        this.sqlBuilder.setAndWhere(this.setting.mid, {            value: mid,            operate: '=',        });        this.sqlBuilder.setAndWhere(this.setting.pid, {            value: pid,            operate: '=',        });        this.sqlBuilder.orderBy = [[this.setting.order, 'DESC']];        const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);        const resultData = await this.db.queryOne(sql, sqlParam);        return resultData;    }    /**     * 根据 父节点id 和 节点排序order 获取数据     *     * @param {Number} mid - master id     * @param {Number} pid - 父节点id     * @param {Number|Array} order - 排序     * @return {Object|Array} - 查询结果     */    async getDataByParentAndOrder(mid, pid, order) {        const result = await this.db.select(this.tableName, {            where: this.getCondition({mid: mid, pid: pid, order: order})        });        return order instanceof Array ? result : (result.length > 0 ? result[0] : null);    }    async getChildBetween(mid, pid, order1, order2) {        this.initSqlBuilder();        this.sqlBuilder.setAndWhere(this.setting.mid, {            value: mid,            operate: '=',        });        this.sqlBuilder.setAndWhere(this.setting.pid, {            value: pid,            operate: '=',        });        this.sqlBuilder.setAndWhere(this.setting.order, {            value: order1,            operate: '>',        });        this.sqlBuilder.setAndWhere(this.setting.order, {            value: order2,            operate: '<',        });        this.sqlBuilder.orderBy = [[this.setting.order, 'ASC']];        const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);        const data = await this.db.query(sql, sqlParam);        return data;    }    /**     * 根据 父节点ID 和 节点排序order 获取全部后节点数据     * @param {Number} mid - master id     * @param {Number} pid - 父节点id     * @param {Number} order - 排序     * @return {Array}     */    async getNextsData(mid, pid, order) {        this.initSqlBuilder();        this.sqlBuilder.setAndWhere(this.setting.mid, {            value: mid,            operate: '=',        });        this.sqlBuilder.setAndWhere(this.setting.pid, {            value: pid,            operate: '=',        });        this.sqlBuilder.setAndWhere(this.setting.order, {            value: order,            operate: '>',        });        this.sqlBuilder.orderBy = [[this.setting.order, 'ASC']];        const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);        const data = await this.db.query(sql, sqlParam);        return data;    }    /**     * 根据fullPath获取数据 fullPath Like ‘1.2.3%’(传参fullPath = '1.2.3%')     * @param {Number} tenderId - 标段id     * @param {String} fullPath - 路径     * @return {Promise<void>}     */    async getDataByFullPath(mid, fullPath) {        this.initSqlBuilder();        this.sqlBuilder.setAndWhere(this.setting.mid, {            value: mid,            operate: '=',        });        this.sqlBuilder.setAndWhere(this.setting.fullPath, {            value: this.db.escape(fullPath),            operate: 'Like',        });        const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);        const resultData = await this.db.query(sql, sqlParam);        return resultData;    }    /**     * 根据fullPath检索自己及所有父项     * @param {Number} tenderId - 标段id     * @param {Array|String} fullPath - 节点完整路径     * @return {Promise<*>}     * @private     */    async getFullLevelDataByFullPath(mid, fullPath) {        const explodePath = this.ctx.helper.explodePath(fullPath);        this.initSqlBuilder();        this.sqlBuilder.setAndWhere(this.setting.mid, {            value: mid,            operate: '=',        });        this.sqlBuilder.setAndWhere(this.setting.fullPath, {            value: explodePath,            operate: 'in',        });        const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);        const data = await this.db.query(sql, sqlParam);        return data;    }    /**     * 根据 父节点id 获取子节点     * @param tenderId     * @param nodeId     * @return {Promise<*>}     */    async getChildrenByParentId(mid, pid) {        if (mid <= 0 || !pid) return undefined;        const pids = pid instanceof Array ? pid : [pid];        this.initSqlBuilder();        this.sqlBuilder.setAndWhere(this.setting.mid, {            value: mid,            operate: '=',        });        this.sqlBuilder.setAndWhere(this.setting.pid, {            value: pids,            operate: 'in',        });        this.sqlBuilder.orderBy = [[this.setting.order, 'ASC']];        const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);        const data = await this.db.query(sql, sqlParam);        return data;    }    /**     * 根据 父节点id 获取孙子节点     * @param tenderId     * @param nodeId     * @return {Promise<void>}     */    async getPosterityByParentId(mid, pid) {        if (mid <= 0 || !pid) return undefined;        const node = await this.getDataByKid(mid, pid);        this.initSqlBuilder();        this.sqlBuilder.setAndWhere(this.setting.mid, {            value: mid,            operate: '=',        });        this.sqlBuilder.setAndWhere(this.setting.fullPath, {            value: this.db.escape(node[this.setting.fullPath] + '-%'),            operate: 'like',        });        const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);        const data = await this.db.query(sql, sqlParam);        return data;    }    async getImportInfo(mid) {        const maxId = await this._getMaxLid(mid);        return { maxId };    }    /**     * 获取最大节点id     *     * @param {Number} mid - master id     * @return {Number}     * @private     */    async _getMaxLid(mid) {        const cacheKey = this.setting.keyPre + mid;        let maxId = parseInt(await this.cache.get(cacheKey));        // 判断是否存在变更新增部位maxId,有则取两者最大值        const changeReviseCacheKey = 'change_ledger_maxLid2:' + mid;        const changeReviseMaxId = await this.cache.get(changeReviseCacheKey);        if (changeReviseMaxId) {            maxId = !maxId ? parseInt(changeReviseMaxId) : Math.max(maxId, parseInt(changeReviseMaxId));        }        if (!maxId) {            const sql = 'SELECT Max(??) As max_id FROM ?? Where ' + this.setting.mid + ' = ?';            const sqlParam = [this.setting.kid, this.tableName, mid];            const queryResult = await this.db.queryOne(sql, sqlParam);            maxId = queryResult.max_id || 0;            this.cache.set(cacheKey, maxId, 'EX', this.ctx.app.config.cacheTime);        }        return maxId;    }    /**     * 缓存最大节点id     *     * @param {Number} mid - master id     * @param {Number} maxId - 当前最大节点id     * @returns {Promise<void>}     * @private     */    _cacheMaxLid(mid, maxId) {        this.cache.set(this.setting.keyPre + mid , maxId, 'EX', this.ctx.app.config.cacheTime);    }    /**     * 移除最大节点id     *     * @param {Number} mid - master id     * @return {Number}     * @private     */    async _removeCacheMaxLid(mid) {        return await this.cache.del(this.setting.keyPre + mid);    }    /**     * 更新order     * @param {Number} mid - master id     * @param {Number} pid - 父节点id     * @param {Number} order - 开始更新的order     * @param {Number} incre - 更新的增量     * @returns {Promise<*>}     * @private     */    async _updateChildrenOrder(mid, pid, order, incre = 1) {        this.initSqlBuilder();        this.sqlBuilder.setAndWhere(this.setting.mid, {            value: mid,            operate: '=',        });        this.sqlBuilder.setAndWhere(this.setting.order, {            value: order,            operate: '>=',        });        this.sqlBuilder.setAndWhere(this.setting.pid, {            value: pid,            operate: '=',        });        this.sqlBuilder.setUpdateData(this.setting.order, {            value: Math.abs(incre),            selfOperate: incre > 0 ? '+' : '-',        });        const [sql, sqlParam] = this.sqlBuilder.build(this.tableName, 'update');        const data = await this.transaction.query(sql, sqlParam);        return data;    }    /**     * 新增数据(新增为selectData的后项,该方法不可单独使用)     *     * @param {Number} mid - 台账id     * @param {Object} select - 选中节点的数据     * @param {Object} data - 新增节点的初始数据     * @return {Object} - 新增结果     * @private     */    async _addNodeData(mid, select, data) {        if (!data) {            data = {};        }        const maxId = await this._getMaxLid(mid);        if (this.setting.uuid) data.id = this.uuid.v4();        data[this.setting.kid] = maxId + 1;        data[this.setting.pid] = select ? select[this.setting.pid] : this.rootId;        data[this.setting.mid] = mid;        data[this.setting.level] = select ? select[this.setting.level] : 1;        data[this.setting.order] = select ? select[this.setting.order] + 1 : 1;        data[this.setting.fullPath] = data[this.setting.level] > 1 ? select[this.setting.fullPath].replace('-' + select[this.setting.kid], '-' + data[this.setting.kid]) : data[this.setting.kid] + '';        data[this.setting.isLeaf] = true;        const result = await this.transaction.insert(this.tableName, data);        this._cacheMaxLid(mid, maxId + 1);        return result;    }    /**     * 新增节点     * @param {Number} mid - 台账id     * @param {Number} kid - 清单节点id     * @returns {Promise<void>}     */    async addNode(mid, kid, data) {        if (!mid) return null;        const select = kid ? await this.getDataByKid(mid, kid) : null;        if (kid && !select) throw '新增节点数据错误';        this.transaction = await this.db.beginTransaction();        try {            if (select) await this._updateChildrenOrder(mid, select[this.setting.pid], select[this.setting.order]+1);            const newNode = await this._addNodeData(mid, select, data);            if (newNode.affectedRows !== 1) throw '新增节点数据错误';            await this.transaction.commit();            this.transaction = null;        } catch (err) {            await this.transaction.rollback();            this.transaction = null;            throw err;        }        if (select) {            const createData = await this.getDataByParentAndOrder(mid, select[this.setting.pid], [select[this.setting.order] + 1]);            const updateData = await this.getNextsData(mid, select[this.setting.pid], select[this.setting.order] + 1);            return {create: createData, update: updateData};        } else {            const createData = await this.getDataByParentAndOrder(mid, -1, [1]);            return {create: createData};        }    }    async addNodeBatch(mid, kid, data, count = 1) {        if (!mid) return null;        const select = kid ? await this.getDataByKid(mid, kid) : null;        if (kid && !select) throw '新增节点数据错误';        this.transaction = await this.db.beginTransaction();        try {            if (select) await this._updateChildrenOrder(mid, select[this.setting.pid], select[this.setting.order] + 1, count);            const newDatas = [];            const maxId = await this._getMaxLid(mid);            for (let i = 1; i < count + 1; i++) {                const newData = Object.assign({}, data);                if (this.setting.uuid) newData.id = this.uuid.v4();                newData[this.setting.kid] = maxId + i;                newData[this.setting.pid] = select ? select[this.setting.pid] : this.rootId;                newData[this.setting.mid] = mid;                newData[this.setting.level] = select ? select[this.setting.level] : 1;                newData[this.setting.order] = select ? select[this.setting.order] + i : i;                newData[this.setting.fullPath] = newData[this.setting.level] > 1                    ? select[this.setting.fullPath].replace('-' + select[this.setting.kid], '-' + newData[this.setting.kid])                    : newData[this.setting.kid] + '';                newData[this.setting.isLeaf] = true;                newDatas.push(newData);            }            const insertResult = await this.transaction.insert(this.tableName, newDatas);            this._cacheMaxLid(mid, maxId + count);            if (insertResult.affectedRows !== count) throw '新增节点数据错误';            await this.transaction.commit();            this.transaction = null;        } catch (err) {            await this.transaction.rollback();            this.transaction = null;            throw err;        }        if (select) {            const createData = await this.getChildBetween(mid, select[this.setting.pid], select[this.setting.order], select[this.setting.order] + count + 1);            const updateData = await this.getNextsData(mid, select[this.setting.pid], select[this.setting.order] + count);            return {create: createData, update: updateData};        } else {            const createData = await this.getChildBetween(mid, -1, 0, count + 1);            return {create: createData};        }    }    /**     * 删除相关数据 用于继承     * @param mid     * @param deleteData     * @returns {Promise<void>}     * @private     */    async _deleteRelaData(mid, deleteData) {    }    /**     * 删除节点     * @param {Number} tenderId - 标段id     * @param {Object} deleteData - 删除节点数据     * @return {Promise<*>}     * @private     */    async _deletePosterity(mid, node) {        this.initSqlBuilder();        this.sqlBuilder.setAndWhere(this.setting.mid, {            value: mid,            operate: '=',        });        this.sqlBuilder.setAndWhere(this.setting.fullPath, {            value: this.db.escape(node[this.setting.fullPath] + '-%'),            operate: 'Like',        });        const [sql, sqlParam] = this.sqlBuilder.build(this.tableName, 'delete');        const result = await this.transaction.query(sql, sqlParam);        return result;    }    /**     *  tenderId标段中, 删除选中节点及其子节点     *     * @param {Number} tenderId - 标段id     * @param {Number} selectId - 选中节点id     * @return {Array} - 被删除的数据     */    async deleteNode(mid, kid) {        if ((mid <= 0) || (kid <= 0)) return [];        const select = await this.getDataByKid(mid, kid);        if (!select) throw '删除节点数据错误';        const parent = await this.getDataByKid(mid, select[this.setting.pid]);        // 获取将要被删除的数据        const deleteData = await this.getDataByFullPath(mid, select[this.setting.fullPath] + '-%');        deleteData.unshift(select);        if (deleteData.length === 0) throw '删除节点数据错误';        this.transaction = await this.db.beginTransaction();        try {            // 删除            await this.transaction.delete(this.tableName, { id: select.id });            const operate = await this._deletePosterity(mid, select);            // 选中节点--父节点 只有一个子节点时,应升级isLeaf            if (parent) {                const count = await this.db.count(this.tableName, this.getCondition({mid: mid, pid: select[this.setting.pid]}));                if (count === 1) {                    const updateParent = {id: parent.id };                    updateParent[this.setting.isLeaf] = true;                    await this.transaction.update(this.tableName, updateParent);                }            }            // 选中节点--全部后节点 order--            await this._updateChildrenOrder(mid, select[this.setting.pid], select[this.setting.order] + 1, -1);            // 删除部位明细            await this._deleteRelaData(mid, deleteData);            await this.transaction.commit();            this.transaction = null;        } catch (err) {            await this.transaction.rollback();            this.transaction = null;            throw err;        }        // 查询结果        const updateData = await this.getNextsData(mid, select[this.setting.pid], select[this.setting.order] - 1);        if (parent) {            const updateData1 = await this.getDataByKid(mid, select[this.setting.pid]);            if (updateData1[this.setting.isLeaf]) {                updateData.push(updateData1);            }        }        return { delete: deleteData, update: updateData };    }    async deleteNodes(mid, kid, count) {        if ((mid <= 0) || (kid <= 0) || (count <= 0)) return [];        const selects = await this.getDataByKidAndCount(mid, kid, count);        const first = selects[0];        const parent = await this.getDataByKid(mid, first[this.setting.pid]);        const childCount = parent ? await this.count(this.getCondition({mid: mid, pid: parent[this.setting.kid]})) : -1;        let deleteData = [];        for (const s of selects) {            deleteData = deleteData.concat(await this.getDataByFullPath(mid, s[this.setting.fullPath] + '-%'));            deleteData.push(s);        }        this.transaction = await this.db.beginTransaction();        try {            // 删除            await this.transaction.delete(this.tableName, { id: selects.map(x => { return x.id }) });            for (const s of selects) {                const operate = await this._deletePosterity(mid, s);            }            // 选中节点--父节点 只有一个子节点时,应升级isLeaf            if (parent && childCount === count) {                const updateParent = {id: parent.id };                updateParent[this.setting.isLeaf] = true;                await this.transaction.update(this.tableName, updateParent);            }            // 选中节点--全部后节点 order--            await this._updateChildrenOrder(mid, first[this.setting.pid], first[this.setting.order] + count, -count);            await this.transaction.commit();            this.transaction = null;            const updateData = await this.getNextsData(mid, first[this.setting.pid], first[this.setting.order] - 1);            if (parent && childCount === count) {                const updateData1 = await this.getDataByKid(mid, parent[this.setting.kid]);                updateData.push(updateData1);            }            return { delete: deleteData, update: updateData };        } catch (err) {            if (this.transaction) {                await this.transaction.rollback();                this.transaction = null;            }            throw err;        }    }    async delete(mid, kid, count) {        if (count && count > 1) {            return await this.deleteNodes(mid, kid, count);        } else {            return await this.deleteNode(mid, kid);        }    }    /**     * 上移节点     *     * @param {Number} mid - master id     * @param {Number} kid - 选中节点id     * @return {Array} - 发生改变的数据     */    async upMoveNode(mid, kid, count) {        if (!count) count = 1;        if (!mid || (mid <= 0) || !kid || (kid <= 0)) return null;        const selects = await this.getDataByKidAndCount(mid, kid, count);        if (selects.length !== count) throw '上移节点数据错误';        const first = selects[0];        const pre = await this.getDataByParentAndOrder(mid, first[this.setting.pid], first[this.setting.order] - 1);        if (!pre) throw '节点不可上移';        const order = [];        this.transaction = await this.db.beginTransaction();        try {            for (const s of selects) {                const sData = { id: s.id };                sData[this.setting.order] = s[this.setting.order] - 1;                await this.transaction.update(this.tableName, sData);                order.push(s[this.setting.order] - 1);            }            const pData = { id: pre.id };            pData[this.setting.order] = pre[this.setting.order] + count;            await this.transaction.update(this.tableName, pData);            order.push(pre[this.setting.order] + count);            await this.transaction.commit();            this.transaction = null;        } catch (err) {            await this.transaction.rollback();            this.transaction = null;            throw err;        }        const resultData = await this.getDataByParentAndOrder(mid, first[this.setting.pid], order);        return { update: resultData };    }    /**     * 下移节点     *     * @param {Number} mid - master id     * @param {Number} kid - 选中节点id     * @return {Array} - 发生改变的数据     */    async downMoveNode(mid, kid, count) {        if (!count) count = 1;        if (!mid || (mid <= 0) || !kid || (kid <= 0)) return null;        const selects = await this.getDataByKidAndCount(mid, kid, count);        if (selects.length !== count) {            throw '下移节点数据错误';        }        const last = selects[count - 1];        const next = await this.getDataByParentAndOrder(mid, last[this.setting.pid], last[this.setting.order] + 1);        if (!next) {            throw '节点不可下移';        }        const order = [];        this.transaction = await this.db.beginTransaction();        try {            for (const s of selects) {                const sData = { id: s.id };                sData[this.setting.order] = s[this.setting.order] + 1;                await this.transaction.update(this.tableName, sData);                order.push(s[this.setting.order] + 1);            }            const nData = { id: next.id };            nData[this.setting.order] = next[this.setting.order] - count;            await this.transaction.update(this.tableName, nData);            order.push(next[this.setting.order] - count);            await this.transaction.commit();            this.transaction = null;        } catch (err) {            await this.transaction.rollback();            this.transaction = null;            throw err;        }        const resultData = await this.getDataByParentAndOrder(mid, last[this.setting.pid], order);        return { update: resultData };    }    /**     * 节点成为父项时,可能需要修改父项数据,供子类继承     * @param data     */    clearParentingData (data) {    }    /**     * 升级selectData, 同步修改所有子节点     * @param {Object} selectData - 升级操作,选中节点     * @return {Object}     * @private     */    async _syncUplevelChildren(select) {        this.initSqlBuilder();        this.sqlBuilder.setAndWhere(this.setting.mid, {            value: select[this.setting.mid],            operate: '=',        });        this.sqlBuilder.setAndWhere(this.setting.fullPath, {            value: this.db.escape(select[this.setting.fullPath] + '-%'),            operate: 'like',        });        this.sqlBuilder.setUpdateData(this.setting.level, {            value: 1,            selfOperate: '-',        });        this.sqlBuilder.setUpdateData(this.setting.fullPath, {            value: [this.setting.fullPath, this.db.escape(`-${select[this.setting.pid]}-`), this.db.escape('-')],            literal: 'Replace',        });        const [sql, sqlParam] = this.sqlBuilder.build(this.tableName, 'update');        const data = await this.transaction.query(sql, sqlParam);        return data;    }    /**     * 选中节点的后兄弟节点,全部变为当前节点的子节点     * @param {Object} selectData - 选中节点     * @return {Object}     * @private     */    async _syncUpLevelNexts(select) {        // 查询selectData的lastChild        const lastChild = await this.getLastChildData(select[this.setting.mid], select[this.setting.kid]);        const nexts = await this.getNextsData(select[this.setting.mid], select[this.setting.pid], select[this.setting.order]);        if (nexts && nexts.length > 0) {            // 修改nextsData pid, 排序            this.initSqlBuilder();            this.sqlBuilder.setUpdateData(this.setting.pid, {                value: select[this.setting.kid],            });            const orderInc = lastChild ? lastChild[this.setting.order] - select[this.setting.order] : - select[this.setting.order];            this.sqlBuilder.setUpdateData(this.setting.order, {                value: Math.abs(orderInc),                selfOperate: orderInc > 0 ? '+' : '-',            });            this.sqlBuilder.setAndWhere(this.setting.mid, {                value: select[this.setting.mid],                operate: '=',            });            this.sqlBuilder.setAndWhere(this.setting.pid, {                value: select[this.setting.pid],                operate: '=',            });            this.sqlBuilder.setAndWhere(this.setting.order, {                value: select[this.setting.order],                operate: '>',            });            const [sql1, sqlParam1] = this.sqlBuilder.build(this.tableName, 'update');            await this.transaction.query(sql1, sqlParam1);            // 选中节点 isLeaf应为false            if (select[this.setting.isLeaf]) {                const updateData = { id: select.id };                updateData[this.setting.isLeaf] = false;                await this.transaction.update(this.tableName, updateData);            }            // 修改nextsData及其子节点的fullPath            const oldSubStr = this.db.escape(select[this.setting.pid] + '-');            const newSubStr = this.db.escape(select[this.setting.kid] + '-');            const sqlArr = [];            sqlArr.push('Update ?? SET `' + this.setting.fullPath + '` = Replace(`' + this.setting.fullPath + '`,' + oldSubStr + ',' + newSubStr + ') Where');            sqlArr.push('(`' + this.setting.mid + '` = ' + select[this.setting.mid] + ')');            sqlArr.push(' And (');            for (const data of nexts) {                sqlArr.push('`' + this.setting.fullPath + '` Like ' + this.db.escape(data[this.setting.fullPath] + '%'));                if (nexts.indexOf(data) < nexts.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(mid, kid, count) {        if (!count) count = 1;        const selects = await this.getDataByKidAndCount(mid, kid, count);        if (selects.length !== count) throw '升级节点数据错误';        const first = selects[0], last = selects[count - 1];        const parent = await this.getDataByKid(mid, first[this.setting.pid]);        if (!parent) throw '升级节点数据错误';        const newPath = [];        this.transaction = await this.db.beginTransaction();        try {            // 选中节点--父节点 选中节点为firstChild时,修改isLeaf            if (first[this.setting.order] === 1) {                const updateParentData = { id: parent.id };                updateParentData[this.setting.isLeaf] = true;                await this.transaction.update(this.tableName, updateParentData);            }            // 选中节点--父节点--全部后兄弟节点 order+1            await this._updateChildrenOrder(mid, parent[this.setting.pid], parent[this.setting.order] + 1, count);            for (const [i, s] of selects.entries()) {                // 选中节点 修改pid, order, fullPath, level, isLeaf, 清空计算项                const updateData = { id: s.id };                updateData[this.setting.pid] = parent[this.setting.pid];                updateData[this.setting.order] = parent[this.setting.order] + i + 1;                updateData[this.setting.level] = s[this.setting.level] - 1;                updateData[this.setting.fullPath] = s[this.setting.fullPath].replace(`-${s[this.setting.pid]}-`, '-');                newPath.push(updateData[this.setting.fullPath]);                if (s[this.setting.isLeaf] && s.id === last.id) {                    const nexts = await this.getNextsData(mid, parent[this.setting.kid], last[this.setting.order]);                    if (nexts.length > 0) {                        updateData[this.setting.isLeaf] = false;                        this.clearParentingData(updateData);                    }                }                await this.transaction.update(this.tableName, updateData);                // 选中节点--全部子节点(含孙) level-1, fullPath变更                await this._syncUplevelChildren(s);            }            // 选中节点--全部后兄弟节点 收编为子节点 修改pid, order, fullPath            await this._syncUpLevelNexts(last);            await this.transaction.commit();            this.transaction = null;        } catch (err) {            await this.transaction.rollback();            this.transaction = null;            throw err;        }        // 查询修改的数据        let updateData = await this.getNextsData(mid, parent[this.setting.pid], parent[this.setting.order] - 1);        for (const path of newPath) {            const children = await this.getDataByFullPath(mid, path + '-%');            updateData = updateData.concat(children);        }        return { update: updateData };    }    /**     * 降级selectData, 同步修改所有子节点     * @param {Object} selectData - 选中节点     * @param {Object} preData - 选中节点的前一节点(降级后为父节点)     * @return {Promise<*>}     * @private     */    async _syncDownlevelChildren(select, newFullPath) {        this.initSqlBuilder();        this.sqlBuilder.setAndWhere(this.setting.mid, {            value: select[this.setting.mid],            operate: '=',        });        this.sqlBuilder.setAndWhere(this.setting.fullPath, {            value: this.db.escape(select[this.setting.fullPath] + '-%'),            operate: 'like',        });        this.sqlBuilder.setUpdateData(this.setting.level, {            value: 1,            selfOperate: '+',        });        this.sqlBuilder.setUpdateData(this.setting.fullPath, {            value: [this.setting.fullPath, this.db.escape(select[this.setting.fullPath] + '-'), this.db.escape(newFullPath + '-')],            literal: 'Replace',        });        const [sql, sqlParam] = this.sqlBuilder.build(this.tableName, 'update');        const data = await this.transaction.query(sql, sqlParam);        return data;    }    /**     * 降级节点     *     * @param {Number} tenderId - 标段id     * @param {Number} selectId - 选中节点id     * @return {Array} - 发生改变的数据     */    async downLevelNode(mid, kid, count) {        if (!count) count = 1;        const selects = await this.getDataByKidAndCount(mid, kid, count);        if (!selects) throw '降级节点数据错误';        const first = selects[0], last = selects[count - 1];        const pre = await this.getDataByParentAndOrder(mid, first[this.setting.pid], first[this.setting.order] - 1);        if (!pre) throw '节点不可降级';        const preLastChild = await this.getLastChildData(mid, pre[this.setting.kid]);        const newPath = [];        this.transaction = await this.db.beginTransaction();        try {            // 选中节点--全部后节点 order--            await this._updateChildrenOrder(mid, first[this.setting.pid], last[this.setting.order] + 1, -count);            for (const [i, s] of selects.entries()) {                // 选中节点 修改pid, level, order, fullPath                const updateData = { id: s.id };                updateData[this.setting.pid] = pre[this.setting.kid];                updateData[this.setting.order] = preLastChild ? preLastChild[this.setting.order] + i + 1 : i + 1;                updateData[this.setting.level] = s[this.setting.level] + 1;                if (s[this.setting.level] === 1) {                    updateData[this.setting.fullPath] = pre[this.setting.kid] + '-' + s[this.setting.kid];                } else {                    const index = s[this.setting.fullPath].lastIndexOf(s[this.setting.kid]);                    updateData[this.setting.fullPath] = s[this.setting.fullPath].substring(0, index-1) + '-' + pre[this.setting.kid] + '-' + s[this.setting.kid];                }                newPath.push(updateData[this.setting.fullPath]);                await this.transaction.update(this.tableName, updateData);                // 选中节点--全部子节点(含孙) level++, fullPath                await this._syncDownlevelChildren(s, updateData[this.setting.fullPath]);            }            // 选中节点--前兄弟节点 isLeaf应为false, 清空计算相关字段            const updateData2 = { id: pre.id };            updateData2[this.setting.isLeaf] = false;            this.clearParentingData(updateData2);            await this.transaction.update(this.tableName, updateData2);            await this.transaction.commit();            this.transaction = null;        } catch (err) {            await this.transaction.rollback();            this.transaction = null;            throw err;        }        // 查询修改的数据        let updateData = await this.getNextsData(mid, pre[this.setting.pid], pre[this.setting.order] - 1);        // 选中节点及子节点        for (const p of newPath) {            updateData = updateData.concat(await this.getDataByFullPath(mid, p + '-%'));        }        updateData = updateData.concat(await this.getDataById(selects.map(x => { return x.id; })));        // 选中节点--原前兄弟节点&全部后兄弟节点        return { update: updateData };    }    /**     * 过滤data中update方式不可提交的字段     * @param {Number} id - 主键key     * @param {Object} data     * @return {Object<{id: *}>}     * @private     */    _filterUpdateInvalidField(id, data) {        const result = {id: id};        for (const prop in data) {            if (this.readOnlyFields.indexOf(prop) === -1) {                result[prop] = data[prop];            }        }        return result;    }    /**     * 提交多条数据 - 不影响计算等未提交项     * @param {Number} tenderId - 标段id     * @param {Array} datas - 提交数据     * @return {Array} - 提交后的数据     */    async updateInfos(mid, datas) {        if (mid <= 0) throw '数据错误';        if (Array.isArray(datas)) {            const updateDatas = [];            for (const d of datas) {                if (mid !== d[this.setting.mid]) throw '提交数据错误';                const node = await this.getDataById(d.id);                if (!node || mid !== node[this.setting.mid] || d[this.setting.kid] !== node[this.setting.kid]) throw '提交数据错误';                updateDatas.push(this._filterUpdateInvalidField(node.id, d));            }            await this.db.updateRows(this.tableName, updateDatas);            const resultData = await this.getDataById(this._.map(datas, 'id'));            return resultData;        } else {            if (mid !== datas[this.setting.mid]) throw '提交数据错误';            const node = await this.getDataById(datas.id);            if (!node || mid !== node[this.setting.mid] || datas[this.setting.kid] !== node[this.setting.kid]) throw '提交数据错误';            const updateData = this._filterUpdateInvalidField(node.id, datas);            await this.db.update(this.tableName, updateData);            return await this.getDataById(datas.id);        }    }}module.exports = TreeService;
 |