'use strict'; const PermissionCheck = require('../const/account_permission').PermissionCheck; // 安全、质量巡检共用附件阶段及操作权限规则。 class InspectionAttachment { constructor(ctx, type) { if (!['safe', 'quality'].includes(type)) throw '无效的巡检类型'; this.ctx = ctx; this.inspectionService = ctx.service[type + 'Inspection']; this.auditService = ctx.service[type + 'InspectionAudit']; this.attachmentService = ctx.service[type + 'InspectionAtt']; } getFileStage(file) { // 尚未回填的历史数据与数据库迁移后的规则保持一致。 return file.flow_stage === null || file.flow_stage === undefined || Number(file.flow_stage) === 0 ? 1 : Number(file.flow_stage); } getUploadStage(inspection) { if (inspection.flow_version === 1) return [1, 2, 3].includes(inspection.flow_stage) ? inspection.flow_stage : 0; // 旧流程只有上报、整改,完成后的补传归入整改阶段。 if ([3, 12].includes(inspection.status)) return 2; return [1, 2, 5, 6].includes(inspection.status) ? 1 : 0; } canUpload(inspection, records) { if (!inspection || inspection.status === 4 || !this.getUploadStage(inspection)) return false; if (inspection.flow_version !== 1) return Boolean(inspection.filePermission); // 页面和上传入口沿用中间件已计算的权限;事务内传入最新审批记录后重新校验。 if (!records && typeof inspection.canUpload === 'boolean') return inspection.canUpload; const uid = this.ctx.session.sessionUser.accountId; if ([1, 3, 5].includes(inspection.status)) return inspection.uid === uid; const current = (records || inspection.auditors || []).filter(record => !record.is_old && record.times === inspection.times && record.flow_stage === inspection.flow_stage && record.stage_round === inspection.stage_round); if (inspection.flow_stage === 2) { current.sort((a, b) => a.audit_order - b.audit_order || a.order - b.order || a.id - b.id); const active = current.find(record => [1, 12].includes(record.status)); return inspection.status === 12 && Boolean(active && active.aid === uid && active.status === 12); } return [2, 6].includes(inspection.status) && current.some(record => record.aid === uid && record.status === 2); } canDeleteFile(file, inspection, records) { if (!file || !inspection || file.tid !== inspection.tid || file.qiid !== inspection.id) return false; if (file.uid !== this.ctx.session.sessionUser.accountId || !this.canUpload(inspection, records)) return false; if (this.getFileStage(file) !== this.getUploadStage(inspection)) return false; return inspection.status !== 3 || Boolean(file.extra_upload) || PermissionCheck.delFile(this.ctx.session.sessionUser.permission); } async getFiles(qiid) { if (this.ctx.inspection) this.ctx.inspection.canUploadAttachments = this.canUpload(this.ctx.inspection); const files = await this.attachmentService.getAllAtt(this.ctx.tender.id, qiid); return files.map(file => Object.assign(file, { flow_stage: this.getFileStage(file), stageName: ['', '上报', '整改', '批复'][this.getFileStage(file)] || '未知阶段', canDelete: this.canDeleteFile(file, this.ctx.inspection), })); } async saveFiles(files, flowContext) { const transaction = await this.attachmentService.beginTransaction(); try { const { current } = await this.getCurrentContext(transaction, flowContext); if (!files.length) throw '请选择附件'; const payload = files.map(file => Object.assign({}, file, { tid: current.tid, qiid: current.id, uid: this.ctx.session.sessionUser.accountId, flow_stage: this.getUploadStage(current), extra_upload: current.status === 3, })); const result = await this.attachmentService.saveFileMsgToDb(payload, transaction); await transaction.commit(); return result; } catch (err) { await transaction.rollback(); throw err; } } async deleteFile(id, flowContext) { id = Number(id); if (!Number.isSafeInteger(id) || id <= 0) throw '无效的附件'; const transaction = await this.attachmentService.beginTransaction(); try { const { current, records } = await this.getCurrentContext(transaction, flowContext); const file = await this.attachmentService.getMaterialFileById(id, transaction); if (!this.canDeleteFile(file, current, records)) throw '只能删除本人在当前阶段上传的附件'; await this.ctx.app.fujianOss.delete(this.ctx.app.config.fujianOssFolder + file.filepath); await this.attachmentService.delete(id, transaction); await transaction.commit(); } catch (err) { await transaction.rollback(); throw err; } } async getCurrentContext(transaction, flowContext) { const { ctx } = this; const rows = await this.inspectionService.getAllDataForUpdate({ id: ctx.inspection.id, tid: ctx.tender.id }, transaction); const current = rows[0]; if (!current) throw '巡检不存在'; const fields = ['flow_version', 'flow_stage', 'stage_round', 'times', 'status']; if (!flowContext || fields.some(key => Number(flowContext[key]) !== Number(current[key]) || Number(ctx.inspection[key]) !== Number(current[key]))) { throw '流程已更新,请刷新后操作附件'; } current.filePermission = ctx.inspection.filePermission; const records = await this.auditService.getAllDataByCondition({ where: { qiid: current.id, times: current.times, is_old: 0 } }, transaction); if (!this.canUpload(current, records)) throw '当前不可操作附件'; return { current, records }; } } module.exports = InspectionAttachment;