'use strict'; /** * 质量管理 - 工程质量 * * @author Mai * @date 2024/7/22 * @version */ class RuleCheck { sortCondition(condition) { const fieldKey = ['gongxu_name', 'yinbi_name', 'file_count', 'file_name']; condition.forEach(c => { c.fieldKeyIndex = fieldKey.indexOf(c.fieldKey); }); condition.sort((a, b) => { return a.fieldKeyIndex - b.fieldKeyIndex; }); } fileCountCheck(files, check) { if (check.operateKey === '>') return files.length > parseInt(check.value); if (check.operateKey === '=') return files.length === parseInt(check.value); } fileNameCheck(files, check) { const matchName = check.multi ? check.value.split(';') : [check.value]; if (check.operateKey === '=') return files.findIndex(f => { return matchName.indexOf(f.filename) >= 0; }) >= 0; if (check.operateKey === 'has') return files.findIndex(f => { return matchName.findIndex(x => { return f.filename.indexOf(x) >= 0; }) >= 0; }) >= 0; } getAllFiles() { const files = []; if (this.quality.kaigong) files.push(...this.quality.kaigong.files); if (this.quality.gongxu) { for (const gx of this.quality.gongxu.list) { files.push(...gx.files); } } if (this.quality.pingding) { files.push(...this.quality.pingding.files); this.quality.pingding.source_files.forEach(sf => { if (sf.spec_type === 'add') files.push(sf); }); } if (this.quality.jiaogong) files.push(...this.quality.jiaogong.files); // if (this.quality.yinbi) { // for (const yb of this.quality.yinbi.list) { // files.push(...yb.files); // } // } return files; } kaigongCheck(check) { if (!this.quality.kaigong) return false; switch (check.fieldKey) { case 'file_count': return this.fileCountCheck(this.quality.kaigong.files, check); case 'file_name': return this.fileNameCheck(this.quality.kaigong.files, check); default: return false; } } GongxuNameCheck(check) { const matchName = check.multi ? check.value.split(';') : [check.value]; if (check.operateKey === '=') { this.matchGongxu = this.quality.gongxu.list.filter(f => { return matchName.indexOf(f.name) >= 0; }); } if (check.operateKey === 'has') { this.matchGongxu = this.quality.gongxu.list.filter(f => { return matchName.findIndex(x => { return f.name.indexOf(x) >= 0; }) >= 0; }); } return this.matchGongxu.length > 0; } getGongxuFiles() { const files = []; const gongxuList = this.matchGongxu || this.quality.gongxu.list; for (const gx of gongxuList) { files.push(...gx.files); } return files; } gongxuCheck(check) { switch (check.fieldKey) { case 'gongxu_name': return this.GongxuNameCheck(check); case 'file_count': return this.fileCountCheck(this.getGongxuFiles(), check); case 'file_name': return this.fileNameCheck(this.getGongxuFiles(), check); default: return false; } } pingdingCheck(check) { if (!this.quality.pingding) return false; const files = [...this.quality.pingding.files, ...this.quality.pingding.source_files]; switch (check.fieldKey) { case 'file_count': return this.fileCountCheck(files, check); case 'file_name': return this.fileNameCheck(files, check); default: return false; } } jiaogongCheck(check) { if (!this.quality.jiaogong) return false; switch (check.fieldKey) { case 'file_count': return this.fileCountCheck(this.quality.jiaogong.files, check); case 'file_name': return this.fileNameCheck(this.quality.jiaogong.files, check); default: return false; } } YinbiNameCheck(check) { const matchName = check.multi ? check.value.split(';') : [check.value]; if (check.operateKey === '=') { this.matchYinbi = this.quality.yinbi.list.filter(f => { return matchName.indexOf(f.name) >= 0; }); } if (check.operateKey === '>') { this.matchYinbi = this.quality.yinbi.list.filter(f => { return matchName.findIndex(x => { return f.name.indexOf(x) >= 0; }) >= 0; }); } return this.matchYinbi.length > 0; } getYinbiFiles() { const files = []; const yinbiList = this.matchYinbi || this.quality.yinbi.list; for (const yb of yinbiList) { files.push(...yb.files); } return files; } yinbiCheck(check) { switch (check.fieldKey) { case 'gongxu_name': return this.YinbiNameCheck(check); case 'file_count': return this.fileCountCheck(this.getYinbiFiles(), check); case 'file_name': return this.fileNameCheck(this.getYinbiFiles(), check); default: return false; } } allFilesCheck(check) { const allFiles = this.getAllFiles(); switch (check.fieldKey) { case 'file_count': return this.fileCountCheck(allFiles, check); case 'file_name': return this.fileNameCheck(allFiles, check); default: return false; } } conditionCheck(check) { switch (check.blockKey) { case 'kaigong': return this.kaigongCheck(check); case 'gongxu': return this.gongxuCheck(check); case 'pingding': return this.pingdingCheck(check); case 'jiaogong': return this.jiaogongCheck(check); case 'yinbi': return this.yinbiCheck(check); case 'all': return this.allFilesCheck(check); default: return false; } } calcQualityStatus(quality, group) { this.quality = quality; this.group = group; for (const r of group.rules) { if (r.condition.length === 0) return false; if (r.condition.length > 1) this.sortCondition(r.condition); } const match = group.rules.filter(r => { this.matchGongxu = null; this.matchYinbi = null; for (const check of r.condition) { if (!this.conditionCheck(check)) { return false; } } return true; }); if (match.length === 0) return null; const data = {}; for (const m of match) { for (const s of m.push_status) { if (!data[s.field] || s.value > data[s.field]) { data[s.field] = s.value; } } } return data; } } module.exports = app => { class Quality extends app.BaseService { /** * 构造函数 * * @param {Object} ctx - egg全局变量 * @return {void} */ constructor(ctx) { super(ctx); this.tableName = 'quality'; this.editableColumns = ['gxby_status', 'gxby_date', 'gxby_limit', 'dagl_status', 'dagl_limit']; this.blockType = { kaigong: { key: 'kaigong', name: '开工' }, gongxu: { key: 'gongxu', name: '工序' }, jiaogong: { key: 'jiaogong', name: '中间交工' }, pingding: { key: 'pingding', name: '评定' }, yinbi: { key: 'yinbi', name: '隐蔽工程' }, shiyan: { key: 'shiyan', name: '试验检测' }, waiwei: { key: 'waiwei', name: '外委检测' }, sanfang: { key: 'sanfang', name: '第三方检测' }, }; } async saveQualityManage(tid, data) { const quality = await this.getDataByCondition({ tid, rela_type: data.rela_type, rela_id: data.rela_id, rela_name: data.rela_name }); if (quality) { const updateData = { id: quality.id, group_id: data.group_id, update_uid: this.ctx.session.sessionUser.accountId, }; await this.db.update(this.tableName, updateData); updateData.rela_id = data.rela_id; updateData.rela_name = data.rela_name; return updateData; } else { const newQuality = { id: this.uuid.v4(), tid, rela_type: data.rela_type, rela_id: data.rela_id, rela_name: data.rela_name, create_uid: this.ctx.session.sessionUser.accountId, update_uid: this.ctx.session.sessionUser.accountId, group_id: data.group_id, }; await this.db.insert(this.tableName, newQuality); return newQuality; } } async saveQuality(quality, filter, data, conn) { data.update_uid = this.ctx.session.sessionUser.accountId; data.is_used = 1; if (quality) { data.id = quality.id; if (conn) { await conn.update(this.tableName, data); } else { await this.db.update(this.tableName, data); } return data; } else { data.id = this.uuid.v4(); data.tid = this.ctx.tender.id; data.rela_type = filter.rela_type; data.rela_id = filter.rela_id; data.rela_name = filter.rela_name; data.create_uid = this.ctx.session.sessionUser.accountId; if (conn) { await conn.insert(this.tableName, data); } else { await this.db.insert(this.tableName, data); } return data; } } async getQuality(filter) { if (filter.quality_id) return await this.getDataByCondition({ id: filter.quality_id }); if (filter.rela_type && filter.rela_id) return await this.getDataByCondition({ tid: filter.tid || this.ctx.tender.id, rela_id: filter.rela_id, rela_name: filter.rela_name }); throw '参数错误'; } async _loadKgData(quality) { if (!quality.kaigong_time) return; quality.kaigong = {}; for (const p in quality) { if (p.indexOf('kaigong_') !== 0) continue; quality.kaigong[p] = quality[p]; delete quality[p]; } const files = await this.ctx.service.qualityFile.getBlockFiles(quality, 'kaigong'); quality.kaigong.files = files.filter(f => { return !f.spec_type; }); quality.kaigong.qa_files = files.filter(f => { return f.spec_type === 'qa'; }); // todo 加载开工审批数据 } async _loadGxData(quality) { quality.gongxu = {}; // todo 加载工序审批数据 quality.gongxu.list = await this.ctx.service.qualityGongxu.getAllDataByCondition({ where: { quality_id: quality.id }, orders:[['create_time', 'asc']] }); for (const gx of quality.gongxu.list) { gx.canEdit = gx.user_id === this.ctx.session.sessionUser.accountId; gx.files = await this.ctx.service.qualityFile.getBlockFiles(quality, 'gongxu', gx.id); // todo 加载工序步骤审批数据 } } async _loadPdData(quality) { if (!quality.kaigong && quality.gongxu.list.length === 0) return; quality.pingding = {}; for (const p in quality) { if (p.indexOf('pingding_') !== 0) continue; quality.kaigong[p] = quality[p]; delete quality[p]; } const files = await this.ctx.service.qualityFile.getBlockFiles(quality, 'pingding'); quality.pingding.files = files.filter(f => { return !f.spec_type; }); quality.pingding.source_files = files.filter(f => { return f.spec_type === 'add'; }); // addtional if (quality.gongxu) { for (const gx of quality.gongxu.list) { const qaFiles = gx.files.filter(f => { return f.spec_type === 'qa'; }); for (const f of qaFiles) { const qaF = JSON.parse(JSON.stringify(f)); qaF.canEdit = false; quality.pingding.source_files.push(qaF); } } } // todo 加载评定审批数据 } async _loadJgData(quality) { if (!quality.kaigong && quality.gongxu.list.length === 0) return; quality.jiaogong = {}; for (const p in quality) { if (p.indexOf('jiaogong_') !== 0) continue; quality.jiaogong[p] = quality[p]; delete quality[p]; } quality.jiaogong.files = await this.ctx.service.qualityFile.getBlockFiles(quality, 'jiaogong'); } async _loadYbData(quality) { quality.yinbi = {}; quality.yinbi.list = await this.ctx.service.qualityYinbi.getAllDataByCondition({ where: { quality_id: quality.id }, orders:[['create_time', 'asc']] }); for (const yb of quality.yinbi.list) { yb.canEdit = yb.user_id === this.ctx.session.sessionUser.accountId; const files = await this.ctx.service.qualityFile.getBlockFiles(quality, 'yinbi', yb.id); yb.before_files = files.filter(f => { return f.spec_type === 'before'; }); yb.after_files = files.filter(f => { return f.spec_type === 'after'; }); } } async loadQualityDetail(quality) { if (!quality) return; await this._loadKgData(quality); await this._loadGxData(quality); await this._loadPdData(quality); await this._loadJgData(quality); await this._loadYbData(quality); } async getQualityGroup(quality) { if (quality.group_id) return quality.group_id; const ledger = await this.ctx.service.ledger.getDataById(quality.rela_id); const sql = 'SELECT l.id, l.ledger_id, l.level, q.group_id ' + ` FROM ${this.tableName} q LEFT JOIN ${this.ctx.service.ledger.tableName} l ON q.rela_id = l.id ` + ` WHERE q.tid = ${quality.tid} and l.ledger_id in (${ledger.full_path.split('-').join(', ')}) and group_id <> ''` + ' ORDER BY l.level DESC'; const allQuality = await this.db.query(sql); return allQuality.length > 0 ? allQuality[0].group_id : ''; } async checkQualityStatus(quality) { const groupId = await this.getQualityGroup(quality); if (!groupId) return null; const groupRule = await this.ctx.service.qualityRule.getGroupRule(groupId); if (!groupRule) return null; const ruleCheck = new RuleCheck(); return ruleCheck.calcQualityStatus(quality, groupRule); } async checkQualityStatusAndSave(quality) { const data = await this.checkQualityStatus(quality); if (!data) return; for (const field in data) { quality[field] = data[field]; } data.id = quality.id; await this.db.update(this.tableName, data); } async kaigong(filter, name, date) { const quality = await this.getQuality(filter); const conn = await this.db.beginTransaction(); try { const kaigongData = { kaigong_name: name || '开工工序', kaigong_date: date || this.ctx.moment().format('YYYY-MM-DD'), kaigong_uid: this.ctx.session.sessionUser.accountId, kaigong_time: new Date(), }; await this.saveQuality(quality, filter, kaigongData, conn); await conn.commit(); } catch (err) { await conn.rollback(); throw err; } } async delKaigong(filter) { const quality = await this.getQuality(filter); if (!quality || !quality.kaigong_uid) throw '该工程未开工'; if (quality.kaigong_uid !== this.ctx.session.sessionUser.accountId) throw '开工数据不是您新增的,无权删除'; const conn = await this.db.beginTransaction(); try { await this.saveQuality(quality, filter, { kaigong_name: '', kaigong_date: '', kaigong_uid: 0, kaigong_time: null }, conn); await conn.update(this.ctx.service.qualityFile.tableName, { is_deleted: 1 }, { where: { quality_id: quality.id, block_type: 'kaigong' } }); await conn.commit(); } catch (err) { await conn.rollback(); throw err; } } async pushIncStatus(select) { const quality = await this.ctx.service.quality.getAllDataByCondition({ where: { tid: this.ctx.tender.id, rela_id: select, is_used: 1 } }); if (quality.length === 0) return 0; const xmjInsert = [], xmjUpdate = [], posInsert = [], posUpdate = []; const xmjQuality = [], posQuality = []; quality.forEach(q => { if (q.rela_type === 'xmj') { xmjQuality.push(q); } else { posQuality.push(q); } }); const xmjExtra = xmjQuality.length > 0 ? await this.ctx.service.ledgerExtra.getAllDataByCondition({ where: { id: xmjQuality.map(x => { return x.rela_id; }) } }) : []; for (const xq of xmjQuality) { const xe = xmjExtra.find(x => { return x.id === xq.rela_id; }); const wbs_url = `/3f/zh/info?pid=${this.ctx.session.sessionProject.id}&tid=${xq.tid}&${xq.rela_type}id=${xq.rela_id}`; if (xe) { xmjUpdate.push({ id: xe.id, gxby_status: xq.gxby_status, gxby_date: xq.gxby_date, dagl_status: xq.dagl_status, wbs_url }); } else { xmjInsert.push({ id: xq.rela_id, gxby_status: xq.gxby_status, gxby_date: xq.gxby_date, dagl_status: xq.dagl_status, wbs_url }); } } const posExtra = posQuality.length > 0 ? await this.ctx.service.posExtra.getAllDataByCondition({ where: { id: posQuality.map(x => { return x.rela_id; }) } }) : []; for (const pq of posQuality) { const pe = posExtra.find(x => { return x.id === pq.rela_id; }); const wbs_url = `/3f/zh/info?pid=${this.ctx.session.sessionProject.id}&tid=${pq.tid}&${pq.rela_type}id=${pq.rela_id}`; if (pe) { posUpdate.push({ id: pe.id, gxby_status: pq.gxby_status, gxby_date: pq.gxby_date, dagl_status: pq.dagl_status, wbs_url }); } else { posInsert.push({ id: pq.rela_id, gxby_status: pq.gxby_status, gxby_date: pq.gxby_date, dagl_status: pq.dagl_status, wbs_url }); } } const conn = await this.db.beginTransaction(); try { if (xmjInsert.length > 0) await conn.insert(this.ctx.service.ledgerExtra.tableName, xmjInsert); if (xmjUpdate.length > 0) await conn.updateRows(this.ctx.service.ledgerExtra.tableName, xmjUpdate); if (posInsert.length > 0) await conn.insert(this.ctx.service.posExtra.tableName, posInsert); if (posUpdate.length > 0) await conn.updateRows(this.ctx.service.posExtra.tableName, posUpdate); await conn.commit(); } catch (err) { this.ctx.log(err); await conn.rollback(); throw '推送状态错误'; } return xmjInsert.length + xmjUpdate.length + posInsert.length + posUpdate.length; } async pushAllStatus() { const quality = await this.ctx.service.quality.getAllDataByCondition({ where: { tid: this.ctx.tender.id, is_used: 1 } }); if (quality.length === 0) return 0; const xmjQuality = [], posQuality = []; quality.forEach(q => { if (q.rela_type === 'xmj') { xmjQuality.push(q); } else { posQuality.push(q); } }); const xmjInsert = [], xmjUpdate = [], posInsert = [], posUpdate = []; const xmjExtra = await this.ctx.service.ledgerExtra.getAllDataByCondition({ where: { tid: this.ctx.tender.id } }); for (const xq of xmjQuality) { const xe = xmjExtra.find(x => { return x.id === xq.rela_id; }); const wbs_url = `/3f/zh/info?pid=${this.ctx.session.sessionProject.id}&tid=${xq.tid}&${xq.rela_type}id=${xq.rela_id}`; if (xe) { xmjUpdate.push({ id: xe.id, gxby_status: xq.gxby_status, gxby_date: xq.gxby_date, dagl_status: xq.dagl_status, wbs_url }); } else { xmjInsert.push({ id: xq.rela_id, tid: this.ctx.tender.id, gxby_status: xq.gxby_status, gxby_date: xq.gxby_date, dagl_status: xq.dagl_status, wbs_url }); } } const posExtra = await this.ctx.service.posExtra.getAllDataByCondition({ where: { tid: this.ctx.tender.id } }); for (const pq of posQuality) { const pe = posExtra.find(x => { return x.id === pq.rela_id; }); const wbs_url = `/3f/zh/info?pid=${this.ctx.session.sessionProject.id}&tid=${pq.tid}&${pq.rela_type}id=${pq.rela_id}`; if (pe) { posUpdate.push({ id: pe.id, gxby_status: pq.gxby_status, gxby_date: pq.gxby_date, dagl_status: pq.dagl_status, wbs_url }); } else { posInsert.push({ id: pq.rela_id, tid: this.ctx.tender.id, gxby_status: pq.gxby_status, gxby_date: pq.gxby_date, dagl_status: pq.dagl_status, wbs_url }); } } const conn = await this.db.beginTransaction(); try { const defaultData = { gxby_status: -1, gxby_date: null, dagl_status: -1 }; await conn.update(this.ctx.service.ledgerExtra.tableName, defaultData, { where: {tid: this.ctx.tender.id } }); await conn.update(this.ctx.service.posExtra.tableName, defaultData, { where: {tid: this.ctx.tender.id } }); if (xmjInsert.length > 0) await conn.insert(this.ctx.service.ledgerExtra.tableName, xmjInsert); if (xmjUpdate.length > 0) await conn.updateRows(this.ctx.service.ledgerExtra.tableName, xmjUpdate); if (posInsert.length > 0) await conn.insert(this.ctx.service.posExtra.tableName, posInsert); if (posUpdate.length > 0) await conn.updateRows(this.ctx.service.posExtra.tableName, posUpdate); await conn.commit(); } catch (err) { this.ctx.log(err); await conn.rollback(); throw '推送状态错误'; } return xmjInsert.length + xmjUpdate.length + posInsert.length + posUpdate.length; } } return Quality; };