'use strict'; /** * * * @author Mai * @date * @version */ const rootId = '-1'; const imType = require('../const/tender').imType; const defaultFunRela = { banOver: true, hintOver: true, banMinusChangeBills: true, minusNoValue: true, lockPayExpr: false, showMinusCol: true, imType: imType.zl.value, needGcl: false, }; const funSet = require('../const/fun_set'); const defaultFunSet = funSet.defaultInfo; const pageShowConst = require('../const/page_show').defaultSetting; module.exports = app => { class SubProject extends app.BaseService { /** * 构造函数 * * @param {Object} ctx - egg全局变量 * @param {String} tableName - 表名 * @return {void} */ constructor(ctx) { super(ctx); this.tableName = 'sub_project'; // const fileType = [{ key: 'file', value: 1, name: '资料归集'}, { key: 'info_progress', value: 2, name: '项目概况-阶段进度'}] this.FileReferenceType = { file: 1, info_progress: 2}; } /** * 数据规则 * * @param {String} scene - 场景 * @return {Object} - 返回数据规则 */ rule(scene) { let rule = {}; switch (scene) { case 'fun': rule = { imType: {type: 'enum', values: [imType.tz.value, imType.zl.value, imType.bb.value, imType.bw.value], required: true}, banOver: {type: 'bool', required: true,}, hintOver: {type: 'bool', required: true,}, banMinusChangeBills: {type: 'bool', required: true,}, minusNoValue: {type: 'bool', required: true,}, lockPayExpr: {type: 'bool', required: true,}, showMinusCol: {type: 'bool', required: true,}, }; break; default: break; } return rule; } async getSubProject(pid, uid, admin, filterFolder = false) { let result = await this.getAllDataByCondition({ where: { project_id: pid, is_delete: 0 } }); const permission = await this.ctx.service.subProjPermission.getUserPermission(pid, uid); result = result.filter(x => { if (x.is_folder) return !filterFolder; if (admin) return true; const pb = permission.find(y => { return x.id === y.spid}); if (!pb) return false; x.user_permission = pb; // 只要项目下添加了账号,就允许看到项目 return true; // return x.user_permission.budget_permission.length > 0 || x.user_permission.file_permission.length > 0 || x.user_permission.manage_permission.length > 0; }); return result; } _filterEmptyFolder(data) { data.sort((a, b) => { return b.tree_level - a.tree_level}); const result = []; for (const d of data) { if (!d.is_folder) result.push(d); if (result.find(x => { return x.tree_pid === d.id; })) result.push(d); } return result; } async getBudgetProject(pid, uid, admin) { let result = await this.getAllDataByCondition({ where: { project_id: pid, is_delete: 0 } }); const adminPermission = this.ctx.service.subProjPermission.adminPermission; const permission = admin ? [] : await this.ctx.service.subProjPermission.getUserPermission(pid, uid); result = result.filter(x => { if (!x.is_folder && !x.budget_id) return false; if (x.is_folder) return true; if (admin) { x.permission = adminPermission.budget_permission; x.manage_permission = adminPermission.manage_permission; return true; } else { const pb = permission.find(y => { return x.id === y.spid}); if (!pb) return false; x.permission = pb.budget_permission; x.manage_permission = pb.manage_permission; return x.permission.length > 0; } }); return this._filterEmptyFolder(result); } async getFileProject(pid, uid, admin) { let result = await this.getAllDataByCondition({ where: { project_id: pid, is_delete: 0 } }); const adminPermission = this.ctx.service.subProjPermission.adminPermission; const permission = await this.ctx.service.subProjPermission.getUserPermission(pid, uid); result = result.filter(x => { if (!x.is_folder && !x.management) return false; if (x.is_folder) return true; if (admin) { x.permission = adminPermission.file_permission; x.manage_permission = adminPermission.manage_permission; return true; } else { const pb = permission.find(y => { return x.id === y.spid}); if (!pb) return false; x.permission = pb.file_permission; x.manage_permission = pb.manage_permission; return x.permission.length > 0; } }); return this._filterEmptyFolder(result); } async getLastChild(tree_pid) { const result = await this.getAllDataByCondition({ where: { tree_pid, project_id: this.ctx.session.sessionProject.id }, orders: [['tree_order', 'desc']], limit: 1, offset: 0 }); return result[0]; } async getPosterityData(id){ const result = []; let cur = await this.getAllDataByCondition({ where: { tree_pid: id, project_id: this.ctx.session.sessionProject.id } }); let iLevel = 1; while (cur.length > 0 && iLevel < 6) { result.push(...cur); cur = await this.getAllDataByCondition({ where: { tree_pid: cur.map(x => { return x.id })} }); iLevel += 1; } return result; } async getStepNode(node, step) { const tree_order = []; while(step) { tree_order.push(node.tree_order + step); if (step > 0) { step = step - 1; } else { step = step + 1; } } return await this.getAllDataByCondition({ where: { tree_pid: node.tree_pid, tree_order, project_id: this.ctx.session.sessionProject.id }, orders: [['tree_order', 'asc']]}); } async addFolder(data) { const parent = await this.getDataById(data.tree_pid); if (parent && !parent.is_folder) throw '添加数据结构错误'; const lastChild = await this.getLastChild(parent ? parent.id : rootId); const conn = await this.db.beginTransaction(); try { // 获取当前用户信息 const sessionUser = this.ctx.session.sessionUser; // 获取当前项目信息 const sessionProject = this.ctx.session.sessionProject; const insertData = { id: this.uuid.v4(), project_id: sessionProject.id, user_id: sessionUser.accountId, tree_pid: data.tree_pid, tree_level: parent ? parent.tree_level + 1 : 1, tree_order: lastChild ? lastChild.tree_order + 1 : 1, name: data.name, is_folder: 1, }; const operate = await conn.insert(this.tableName, insertData); if (operate.affectedRows === 0) throw '新增文件夹失败'; await conn.commit(); return await this.getSubProject(sessionProject.id, sessionUser.accountId, sessionUser.is_admin); } catch (error) { await conn.rollback(); throw error; } } async addSubProject(data) { const parent = await this.getDataById(data.tree_pid); if (parent && !parent.is_folder) throw '添加数据结构错误'; const lastChild = await this.getLastChild(parent ? parent.id : rootId); const conn = await this.db.beginTransaction(); try { // 获取当前用户信息 const sessionUser = this.ctx.session.sessionUser; // 获取当前项目信息 const sessionProject = this.ctx.session.sessionProject; const insertData = { id: this.uuid.v4(), project_id: sessionProject.id, user_id: sessionUser.accountId, tree_pid: data.tree_pid, tree_level: parent ? parent.tree_level + 1 : 1, tree_order: lastChild ? lastChild.tree_order + 1 : 1, name: data.name, is_folder: 0, }; const operate = await conn.insert(this.tableName, insertData); // todo 根据节点新增时的其他操作 if (operate.affectedRows === 0) throw '新增文件夹失败'; await conn.commit(); return await this.getSubProject(sessionProject.id, sessionUser.accountId, sessionUser.is_admin); } catch (error) { await conn.rollback(); throw error; } } async dragTo(data) { const dragNode = await this.getDataById(data.drag_id); const dropNode = await this.getDataById(data.drop_id); if (!dragNode || !dropNode || !dropNode.is_folder) throw '拖拽数据结构错误'; const lastChild = await this.getLastChild(dropNode.id); const posterity = await this.getPosterityData(dragNode.id); const conn = await this.db.beginTransaction(); try { const updateData = { id: dragNode.id, tree_pid: dropNode.id, tree_level: dropNode.tree_level + 1, tree_order: lastChild ? lastChild.tree_order + 1 : 1, }; await conn.update(this.tableName, updateData); if (dragNode.tree_level !== dropNode.tree_level + 1 && posterity.length > 0) { const posterityUpdateData = posterity.map(x => { return { id: x.id, tree_level: dropNode.tree_level + 1 - dragNode.tree_level + x.tree_level } }); await conn.updateRows(this.tableName, posterityUpdateData); } // 升级原来的后项的order await conn.query(`UPDATE ${this.tableName} SET tree_order = tree_order-1 WHERE tree_pid = ? AND tree_order > ?`, [dragNode.tree_pid, dragNode.tree_order]); await conn.commit(); } catch (error) { await conn.rollback(); throw error; } return await this.getSubProject(this.ctx.session.sessionProject.id, this.ctx.session.sessionUser.accountId, this.ctx.session.sessionUser.is_admin); } async _siblingMove(node, step) { const stepNode = await this.getStepNode(node, step); const conn = await this.db.beginTransaction(); try { const updateData = []; updateData.push({ id: node.id, tree_order: node.tree_order + step }); for (const sn of stepNode) { updateData.push({ id: node.id, tree_order: step > 0 ? sn.tree_order - 1 : sn.tree_order + 1 }); } await conn.updateRows(this.tableName, updateData); await conn.commit(); } catch (error) { await conn.rollback(); throw error; } } async _siblingMoveForce(node, step) { const sibling = await this.getAllDataByCondition({ where: { tree_pid: node.tree_pid, project_id: this.ctx.session.sessionProject.id }, orders: [['tree_order', 'asc']] }); const nodeIndex = sibling.findIndex(x => { return x.id === node.id }); if (nodeIndex + step < 0) throw '移动数据结构错误'; if (nodeIndex + step > sibling.length - 1) throw '移动数据结构错误'; const conn = await this.db.beginTransaction(); try { const updateData = []; updateData.push({ id: node.id, tree_order: sibling[nodeIndex + step].tree_order }); while(step) { const stepNode = sibling[nodeIndex + step]; if (step > 0) { updateData.push({ id: stepNode.id, tree_order: sibling[nodeIndex + step - 1].tree_order }); step = step - 1; } else { updateData.push({ id: stepNode.id, tree_order: sibling[nodeIndex + step + 1].tree_order}); step = step + 1; } } await conn.updateRows(this.tableName, updateData); await conn.commit(); } catch (error) { await conn.rollback(); throw error; } } async _topMove(node) { const lastChild = await this.getLastChild(rootId); const posterity = await this.getPosterityData(node.id); const conn = await this.db.beginTransaction(); try { const updateData = { id: node.id, tree_pid: rootId, tree_level: 1, tree_order: lastChild ? lastChild.tree_order + 1 : 1 }; await conn.update(this.tableName, updateData); if (node.tree_level !== 1 && posterity.length > 0) { const posterityUpdateData = posterity.map(x => { return { id: x.id, tree_level: x.tree_level - node.tree_level + 1 } }); await conn.updateRows(this.tableName, posterityUpdateData); } // 升级原来的后项的order await conn.query(`UPDATE ${this.tableName} SET tree_order = tree_order-1 WHERE tree_pid = ? AND tree_order > ?`, [node.tree_pid, node.tree_order]); await conn.commit(); } catch (error) { await conn.rollback(); throw error; } } async move(data) { const node = await this.getDataById(data.id); if (!node) throw '移动数据结构错误'; switch(data.type) { case 'up': await this._siblingMoveForce(node, -1); break; case 'down': await this._siblingMoveForce(node, 1); break; case 'top': await this._topMove(node); break; default: throw '未知移动类型'; } return await this.getSubProject(this.ctx.session.sessionProject.id, this.ctx.session.sessionUser.accountId, this.ctx.session.sessionUser.is_admin); } async del(id) { const node = await this.getDataById(id); if (!node) throw '删除的数据不存在'; const posterity = await this.getPosterityData(node.id); const updateData = [ { id: node.id, is_delete: 1 }, ]; posterity.forEach(x => { updateData.push({ id: x.id, is_delete: 1}); }); await this.db.updateRows(this.tableName, updateData); return await this.getSubProject(this.ctx.session.sessionProject.id, this.ctx.session.sessionUser.accountId, this.ctx.session.sessionUser.is_admin); } async save(data) { const result = await this.db.update(this.tableName, data); if (result.affectedRows > 0) { return data; } else { throw '更新数据失败'; } } async setBudgetStd(data) { const subProject = await this.getDataById(data.id); const budgetStd = await this.ctx.service.budgetStd.getDataById(data.std_id); if (!budgetStd) throw '选择的概算标准不存在,请刷新页面重试'; const conn = await this.db.beginTransaction(); try { const budget_id = await this.ctx.service.budget.add(conn, { pid: subProject.project_id, user_id: subProject.user_id, rela_tender: subProject.rela_tender }, budgetStd); const updateData = { id: data.id, std_id: budgetStd.id, std_name: budgetStd.name, budget_id }; await conn.update(this.tableName, updateData); await conn.commit(); return updateData; } catch (error) { await conn.rollback(); throw error; } } async setRelaTender(data) { const subProject = await this.getDataById(data.id); const orgRelaTenderId = subProject.rela_tender.split(','); const conn = await this.db.beginTransaction(); try { await conn.update(this.tableName, data); await conn.update(this.ctx.service.budget.tableName, { id: subProject.budget_id, rela_tender: data.rela_tender }); const relaTenderId = data.rela_tender.split(','); const removeTenderId = orgRelaTenderId.filter(x => { return relaTenderId.indexOf(x) < 0}); const addTenderId = relaTenderId.filter(x => { return orgRelaTenderId.indexOf(x) < 0}); if (removeTenderId.length > 0) await conn.update(this.ctx.service.tender.tableName, { spid: '' }, { where: { id: removeTenderId }}); if (addTenderId.length > 0) await conn.update(this.ctx.service.tender.tableName, { spid: data.id }, { where: { id: addTenderId }}); await conn.commit(); return data; } catch (error) { await conn.rollback(); throw error; } } async addRelaTender(transaction, spid, tid) { if (!transaction) throw '未定义事务'; const subProject = await this.getDataById(spid); if (!subProject) throw '所属项目不存在'; const rela = subProject.rela_tender.split(','); if (rela.indexOf(tid + '') >= 0) return; rela.push(tid + ''); const rela_tender = rela.join(','); await transaction.update(this.tableName, { id: spid, rela_tender}); await transaction.update(this.ctx.service.budget.tableName, { id: subProject.budget_id, rela_tender}); } async removeRelaTender(transaction, spid, tid) { if (!transaction) throw '未定义事务'; const subProject = await this.getDataById(spid); if (!subProject) throw '所属项目不存在'; const rela = subProject.rela_tender.split(','); if (rela.indexOf(tid + '') < 0) return; const rela_tender = rela.filter(x => { return x === tid + ''}).join(','); await transaction.update(this.tableName, { id: spid, rela_tender}); await transaction.update(this.ctx.service.budget.tableName, { id: subProject.budget_id, rela_tender}); } async setManagement(data) { const subProject = await this.getDataById(data.id); if (subProject.management === data.management) return data; const users = await this.ctx.service.projectAccount.getAllDataByCondition({ where: { project_id: subProject.project_id, company: data.management }}); const orgMember = await this.ctx.service.subProjPermission.getAllDataByCondition({ where: { spid: subProject.id } }); const dm = [], um = [], im = []; const template = await this.ctx.service.filingTemplateList.getDataById(data.filingTemplate); if (!template) throw '选择的文件类别不存在'; const templateFiling = await this.ctx.service.filingTemplate.getAllDataByCondition({ where: { temp_id: template.id, is_fixed: 1 }, }); const filing_type = this.ctx.service.filing.analysisFilingType(templateFiling).map(x => { return x.value; }).join(','), file_permission = '1,2'; for (const u of users) { const nm = orgMember.find(x => { return u.id === x.uid; }); if (nm) { if (!nm.file_permission) um.push({ id: nm.id, file_permission, filing_type }); } else { im.push({ id: this.uuid.v4(), spid: subProject.id, pid: subProject.project_id, uid: u.id, file_permission, filing_type }); } } const conn = await this.db.beginTransaction(); try { await conn.update(this.tableName, { id: subProject.id, management: data.management, filing_template_id: template.id, filing_template_name: template.name }); await this.ctx.service.filing.initFiling(subProject.id, data.filingTemplate, conn); if (dm.length > 0) await conn.delete(this.ctx.service.subProjPermission.tableName, { id: dm }); if (um.length > 0) await conn.updateRows(this.ctx.service.subProjPermission.tableName, um); if (im.length > 0) await conn.insert(this.ctx.service.subProjPermission.tableName, im); await conn.commit(); return data; } catch (error) { await conn.rollback(); throw error; } } async refreshManagementPermission(data) { const subProject = await this.getDataById(data.id); const users = await this.ctx.service.projectAccount.getAllDataByCondition({ where: { project_id: subProject.project_id, company: subProject.management }}); const orgMember = await this.ctx.service.subProjPermission.getAllDataByCondition({ where: { spid: subProject.id } }); const dm = [], um = [], im = []; const filing_type = this.ctx.service.filing.allFilingType.join(','), file_permission = '1,2'; for (const u of users) { const nm = orgMember.find(x => { return u.id === x.uid; }); if (nm) { if (!nm.file_permission) um.push({ id: nm.id, file_permission, filing_type }); } else { im.push({ id: this.uuid.v4(), spid: subProject.id, pid: subProject.project_id, uid: u.id, file_permission, filing_type }); } } const conn = await this.db.beginTransaction(); try { if (dm.length > 0) await conn.delete(this.ctx.service.subProjPermission.tableName, { id: dm }); if (um.length > 0) await conn.updateRows(this.ctx.service.subProjPermission.tableName, um); if (im.length > 0) await conn.insert(this.ctx.service.subProjPermission.tableName, im); await conn.commit(); return { dm: dm.length, um: um.length, im: im.length }; } catch (error) { await conn.rollback(); throw error; } } // 合同管理获取项目列表 async getSubProjectByContract(pid, uid, admin, filterFolder = false) { let result = await this.getAllDataByCondition({ where: { project_id: pid, is_delete: 0 } }); if (admin) return this._filterEmptyFolder(result); const permission = await this.ctx.service.contractAudit.getAllDataByCondition({ where: { uid } }); result = result.filter(x => { if (x.is_folder) return !filterFolder; const pb = permission.find(y => { return x.id === y.spid; }); if (!pb) return false; return true; }); return this._filterEmptyFolder(result); } async getSubProjectByTender(pid, tenders, filterFolder = false) { if (tenders.length === 0) return []; const spids = this._.uniq(this._.map(tenders, 'spid')); let result = await this.getAllDataByCondition({ where: { project_id: pid, is_delete: 0 } }); result = result.filter(x => { if (x.is_folder) return !filterFolder; if (!x.rela_tender) return false; return this._.includes(spids, x.id); }); return this._filterEmptyFolder(result); } // 合同管理获取项目列表 async getSubProjectByFinancial(pid, uid, admin, filterFolder = false) { let result = await this.getAllDataByCondition({ where: { project_id: pid, is_delete: 0 } }); if (admin) return this._filterEmptyFolder(result); const permission = await this.ctx.service.financialAudit.getAllDataByCondition({ where: { uid } }); result = result.filter(x => { if (x.is_folder) return !filterFolder; const pb = permission.find(y => { return x.id === y.spid; }); if (!pb) return false; return true; }); return this._filterEmptyFolder(result); } async getFileReference(subProject, file_type) { if (file_type) { return await this.db.query(`SELECT id, name FROM zh_file_reference_list WHERE file_type = ?`, [file_type]); } else { return await this.db.query(`SELECT id, name FROM zh_file_reference_list`); } }; getPageShow(page_show) { const info = page_show ? JSON.parse(page_show) : {}; for (const pi in pageShowConst) { info[pi] = info[pi] === undefined ? pageShowConst[pi] : parseInt(info[pi]); this.ctx.helper._.defaults(info[pi], pageShowConst[pi]); } return info; } async updatePageshow(id) { const result = await this.db.update(this.tableName, { id, page_show: JSON.stringify(this.ctx.subProject.page_show), }); return result.affectedRows === 1; } /** * 功能设置 * @param id * @returns {Promise} */ getFunRela(subProject) { const result = subProject.fun_rela ? JSON.parse(subProject.fun_rela) : {}; this.ctx.helper._.defaults(result, defaultFunRela); return result; } async updateFunRela(id, data) { const result = await this.db.update(this.tableName, { id: id, fun_rela: JSON.stringify({ banOver: data.banOver, hintOver: data.hintOver, banMinusChangeBills: data.banMinusChangeBills, imType: data.imType, needGcl: data.needGcl, minusNoValue: data.minusNoValue, lockPayExpr: data.lockPayExpr, showMinusCol: data.showMinusCol, }), }); return result.affectedRows === 1; } getFunSet(fun_set = null) { const result = fun_set ? JSON.parse(fun_set) : {}; this.ctx.helper._.defaults(result, defaultFunSet); return result; } async updateFunSet(id, funSet) { const result = await this.db.update(this.tableName, { id, fun_set: JSON.stringify(funSet), }); return result.affectedRows === 1; } } return SubProject; };