|  | @@ -8,13 +8,18 @@
 | 
	
		
			
				|  |  |   * @version
 | 
	
		
			
				|  |  |   */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -const Service = require('egg').Service;
 | 
	
		
			
				|  |  |  const StageIm = require('../lib/stage_im');
 | 
	
		
			
				|  |  |  const imType = require('../const/tender').imType;
 | 
	
		
			
				|  |  |  const audit = require('../const/audit');
 | 
	
		
			
				|  |  | +// const path = require('path');
 | 
	
		
			
				|  |  | +// const fs = require('fs');
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +const stageImTz = 'mem_stage_im_tz';
 | 
	
		
			
				|  |  | +const stageImTzBills = 'mem_stage_im_tz_bills';
 | 
	
		
			
				|  |  | +const stageImZl = 'mem_stage_im_zl';
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  module.exports = app => {
 | 
	
		
			
				|  |  | -    class ReportMemory extends Service {
 | 
	
		
			
				|  |  | +    class ReportMemory extends app.BaseService {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          /**
 | 
	
		
			
				|  |  |           * 构造函数
 | 
	
	
		
			
				|  | @@ -24,69 +29,117 @@ module.exports = app => {
 | 
	
		
			
				|  |  |           */
 | 
	
		
			
				|  |  |          constructor(ctx) {
 | 
	
		
			
				|  |  |              super(ctx);
 | 
	
		
			
				|  |  | -            this.db = this.app.mysql;
 | 
	
		
			
				|  |  | -            this.cache = this.app.redis;
 | 
	
		
			
				|  |  | -            this._ = this.app._;
 | 
	
		
			
				|  |  | +            this.tableName = 'report_memory';
 | 
	
		
			
				|  |  |              // 需要缓存的数据
 | 
	
		
			
				|  |  |              this.stageImData = null;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          async _checkTender(tid) {
 | 
	
		
			
				|  |  | +            if (this.ctx.tender) return;
 | 
	
		
			
				|  |  |              const tender = await this.ctx.service.tender.getTender(tid);
 | 
	
		
			
				|  |  |              tender.info = await this.ctx.service.tenderInfo.getTenderInfo(tid);
 | 
	
		
			
				|  |  |              this.ctx.tender = tender;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          async _checkStage(sid) {
 | 
	
		
			
				|  |  | -            const status = audit.stage.status;
 | 
	
		
			
				|  |  | -            const stage = await this.ctx.service.stage.getDataById(sid);
 | 
	
		
			
				|  |  | -            stage.auditors = await this.ctx.service.stageAudit.getAuditors(stage.id, stage.times);
 | 
	
		
			
				|  |  | -            stage.curAuditor = await this.ctx.service.stageAudit.getCurAuditor(stage.id, stage.times);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            const accountId = this.ctx.session.sessionUser.accountId, auditorIds = this._.map(stage.auditors, 'aid'), shareIds = [];
 | 
	
		
			
				|  |  | -            if (accountId === stage.user_id) { // 原报
 | 
	
		
			
				|  |  | -                if (stage.curAuditor) {
 | 
	
		
			
				|  |  | -                    stage.readOnly = stage.curAuditor.aid !== accountId;
 | 
	
		
			
				|  |  | -                } else {
 | 
	
		
			
				|  |  | -                    stage.readOnly = stage.status !== status.uncheck && stage.status !== status.checkNo;
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -                stage.curTimes = stage.times;
 | 
	
		
			
				|  |  | -                if (stage.status === status.uncheck || stage.status === status.checkNo) {
 | 
	
		
			
				|  |  | -                    stage.curOrder = 0;
 | 
	
		
			
				|  |  | -                } else if (stage.status === status.checked) {
 | 
	
		
			
				|  |  | -                    stage.curOrder = this._.max(this._.map(stage.auditors, 'order'));
 | 
	
		
			
				|  |  | -                } else {
 | 
	
		
			
				|  |  | -                    stage.curOrder = stage.curAuditor.aid === accountId ? stage.curAuditor.order : stage.curAuditor.order - 1;
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -            } else if (auditorIds.indexOf(accountId) !== -1) { // 审批人
 | 
	
		
			
				|  |  | -                if (stage.status === status.uncheck) {
 | 
	
		
			
				|  |  | -                    throw '您无权查看该数据';
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -                stage.curTimes = stage.status === status.checkNo ? stage.times - 1 : stage.times;
 | 
	
		
			
				|  |  | -                if (stage.status === status.checked) {
 | 
	
		
			
				|  |  | -                    stage.curOrder = this._.max(this._.map(stage.auditors, 'order'));
 | 
	
		
			
				|  |  | -                } else if (stage.status === status.checkNo) {
 | 
	
		
			
				|  |  | -                    const audit = await this.service.stageAudit.getDataByCondition({
 | 
	
		
			
				|  |  | -                        sid: stage.id, times: stage.times - 1, status: status.checkNo
 | 
	
		
			
				|  |  | -                    });
 | 
	
		
			
				|  |  | -                    stage.curOrder = audit.order;
 | 
	
		
			
				|  |  | -                } else {
 | 
	
		
			
				|  |  | -                    stage.curOrder = accountId === stage.curAuditor.aid ? stage.curAuditor.order : stage.curAuditor.order - 1;
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -            } else if (shareIds.indexOf(accountId) !== -1) { // 分享人
 | 
	
		
			
				|  |  | -                if (stage.status === status.uncheck) {
 | 
	
		
			
				|  |  | -                    throw '您无权查看该数据';
 | 
	
		
			
				|  |  | +            if (!this.ctx.stage) {
 | 
	
		
			
				|  |  | +                const status = audit.stage.status;
 | 
	
		
			
				|  |  | +                const stage = await this.ctx.service.stage.getDataById(sid);
 | 
	
		
			
				|  |  | +                stage.auditors = await this.ctx.service.stageAudit.getAuditors(stage.id, stage.times);
 | 
	
		
			
				|  |  | +                stage.curAuditor = await this.ctx.service.stageAudit.getCurAuditor(stage.id, stage.times);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                const accountId = this.ctx.session.sessionUser.accountId, auditorIds = this._.map(stage.auditors, 'aid'), shareIds = [];
 | 
	
		
			
				|  |  | +                if (accountId === stage.user_id) { // 原报
 | 
	
		
			
				|  |  | +                    if (stage.curAuditor) {
 | 
	
		
			
				|  |  | +                        stage.readOnly = stage.curAuditor.aid !== accountId;
 | 
	
		
			
				|  |  | +                    } else {
 | 
	
		
			
				|  |  | +                        stage.readOnly = stage.status !== status.uncheck && stage.status !== status.checkNo;
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                    stage.curTimes = stage.times;
 | 
	
		
			
				|  |  | +                    if (stage.status === status.uncheck || stage.status === status.checkNo) {
 | 
	
		
			
				|  |  | +                        stage.curOrder = 0;
 | 
	
		
			
				|  |  | +                    } else if (stage.status === status.checked) {
 | 
	
		
			
				|  |  | +                        stage.curOrder = this._.max(this._.map(stage.auditors, 'order'));
 | 
	
		
			
				|  |  | +                    } else {
 | 
	
		
			
				|  |  | +                        stage.curOrder = stage.curAuditor.aid === accountId ? stage.curAuditor.order : stage.curAuditor.order - 1;
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                } else if (auditorIds.indexOf(accountId) !== -1) { // 审批人
 | 
	
		
			
				|  |  | +                    if (stage.status === status.uncheck) {
 | 
	
		
			
				|  |  | +                        throw '您无权查看该数据';
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                    stage.curTimes = stage.status === status.checkNo ? stage.times - 1 : stage.times;
 | 
	
		
			
				|  |  | +                    if (stage.status === status.checked) {
 | 
	
		
			
				|  |  | +                        stage.curOrder = this._.max(this._.map(stage.auditors, 'order'));
 | 
	
		
			
				|  |  | +                    } else if (stage.status === status.checkNo) {
 | 
	
		
			
				|  |  | +                        const audit = await this.service.stageAudit.getDataByCondition({
 | 
	
		
			
				|  |  | +                            sid: stage.id, times: stage.times - 1, status: status.checkNo
 | 
	
		
			
				|  |  | +                        });
 | 
	
		
			
				|  |  | +                        stage.curOrder = audit.order;
 | 
	
		
			
				|  |  | +                    } else {
 | 
	
		
			
				|  |  | +                        stage.curOrder = accountId === stage.curAuditor.aid ? stage.curAuditor.order : stage.curAuditor.order - 1;
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                } else if (shareIds.indexOf(accountId) !== -1) { // 分享人
 | 
	
		
			
				|  |  | +                    if (stage.status === status.uncheck) {
 | 
	
		
			
				|  |  | +                        throw '您无权查看该数据';
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                    stage.curTimes = stage.status === status.checkNo ? stage.times - 1 : stage.times;
 | 
	
		
			
				|  |  | +                    stage.curOrder = stage.status === status.checked ? this._.max(this._.map(stage.auditors, 'order')) : stage.curAuditor.order - 1;
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  | -                stage.curTimes = stage.status === status.checkNo ? stage.times - 1 : stage.times;
 | 
	
		
			
				|  |  | -                stage.curOrder = stage.status === status.checked ? this._.max(this._.map(stage.auditors, 'order')) : stage.curAuditor.order - 1;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                this.ctx.stage = stage;
 | 
	
		
			
				|  |  | +                this.ctx.stage.cacheTime = this.ctx.stage.readOnly ? (this.ctx.stage.cache_time_r).getTime(): (this.ctx.stage.cache_time_l).getTime();
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // build-time: 162-384ms, redis-cache: 0-41ms, mysql + IO: 116-146ms
 | 
	
		
			
				|  |  | +        // 一定程度上算是大Value缓存,数据多了以后:
 | 
	
		
			
				|  |  | +        // 1. 达到redis内存阈值时,数据会swap到磁盘,此时将消耗IO时间
 | 
	
		
			
				|  |  | +        // 2. redis单独服务器
 | 
	
		
			
				|  |  | +        // 3. redis集群
 | 
	
		
			
				|  |  | +        async _getReportMemoryCache(name, tid, sid, time) {
 | 
	
		
			
				|  |  | +            // redis
 | 
	
		
			
				|  |  | +            const cacheKey = name + '-t' + tid + (sid ? '-s' + sid : '') + (time ? '-' + time : '');
 | 
	
		
			
				|  |  | +            const data = await this.cache.get(cacheKey);
 | 
	
		
			
				|  |  | +            if (data) {
 | 
	
		
			
				|  |  | +                return eval(data);
 | 
	
		
			
				|  |  | +            } else {
 | 
	
		
			
				|  |  | +                return null;
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            this.ctx.stage = stage;
 | 
	
		
			
				|  |  | +            // mysql + IO
 | 
	
		
			
				|  |  | +            // const rm = await this.getDataByCondition({
 | 
	
		
			
				|  |  | +            //     tid: tid, sid: sid, name: name, time: time
 | 
	
		
			
				|  |  | +            // });
 | 
	
		
			
				|  |  | +            // if (rm && rm.file) {
 | 
	
		
			
				|  |  | +            //     const file = path.join(this.ctx.app.config.filePath, 'report', 'cache', rm.file);
 | 
	
		
			
				|  |  | +            //     if (fs.existsSync(file)) {
 | 
	
		
			
				|  |  | +            //         const data = await fs.readFileSync(file, 'utf8');
 | 
	
		
			
				|  |  | +            //         return eval(data);
 | 
	
		
			
				|  |  | +            //     } else {
 | 
	
		
			
				|  |  | +            //         return null;
 | 
	
		
			
				|  |  | +            //     }
 | 
	
		
			
				|  |  | +            // }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        async _setReportMemoryCache(name, tid, sid, time, data) {
 | 
	
		
			
				|  |  | +            // redis
 | 
	
		
			
				|  |  | +            const cacheKey = name + '-t' + tid + (sid ? '-s' + sid : '') + (time ? '-' + time : '');
 | 
	
		
			
				|  |  | +            this.cache.set(cacheKey, JSON.stringify(data), 'EX', this.ctx.app.config.cacheTime);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            // mysql + IO
 | 
	
		
			
				|  |  | +            // const file = path.join('report', 'cache', 'rm' + (new Date()).getTime() + '.json');
 | 
	
		
			
				|  |  | +            // await this.ctx.helper.saveBufferFile(JSON.stringify(data), path.join(this.ctx.app.config.filePath, file));
 | 
	
		
			
				|  |  | +            // const rm = await this.getDataByCondition({
 | 
	
		
			
				|  |  | +            //     tid: tid, sid: sid, name: name, time: time
 | 
	
		
			
				|  |  | +            // });
 | 
	
		
			
				|  |  | +            // if (rm) {
 | 
	
		
			
				|  |  | +            //     await this.db.update(this.tableName, {id: rm.id, file: file});
 | 
	
		
			
				|  |  | +            // } else {
 | 
	
		
			
				|  |  | +            //     await this.db.insert(this.tableName, {tid: tid, sid: sid, name: name, time: time, file: file});
 | 
	
		
			
				|  |  | +            // }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          async _generateStageIm(tid, sid, isTz = true) {
 | 
	
		
			
				|  |  | -            await this._checkTender(tid);
 | 
	
		
			
				|  |  | -            await this._checkStage(sid);
 | 
	
		
			
				|  |  |              if (isTz && this.ctx.stage.im_type !== imType.tz.value) {
 | 
	
		
			
				|  |  |                  throw '您查看的报表跟设置不符,请查看“总量控制”的报表';
 | 
	
		
			
				|  |  |              } else if (!isTz && this.ctx.stage.im_type === imType.tz.value) {
 | 
	
	
		
			
				|  | @@ -97,6 +150,10 @@ module.exports = app => {
 | 
	
		
			
				|  |  |              this.stageImData.main = stageIm.ImData;
 | 
	
		
			
				|  |  |              if (isTz) {
 | 
	
		
			
				|  |  |                  this.stageImData.bills = stageIm.ImBillsData;
 | 
	
		
			
				|  |  | +                await this._setReportMemoryCache(stageImTz, tid, sid, this.ctx.stage.cacheTime, this.stageImData.main);
 | 
	
		
			
				|  |  | +                await this._setReportMemoryCache(stageImTzBills, tid, sid, this.ctx.stage.cacheTime, this.stageImData.bills);
 | 
	
		
			
				|  |  | +            } else {
 | 
	
		
			
				|  |  | +                await this._setReportMemoryCache(stageImZl, tid, sid, this.ctx.stage.cacheTime, this.stageImData.main);
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -125,6 +182,15 @@ module.exports = app => {
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          async getStageImTzData(tid, sid) {
 | 
	
		
			
				|  |  | +            await this._checkTender(tid);
 | 
	
		
			
				|  |  | +            await this._checkStage(sid);
 | 
	
		
			
				|  |  | +            const cache = await this._getReportMemoryCache('mem_stage_im_tz', tid, sid, this.ctx.stage.cacheTime);
 | 
	
		
			
				|  |  | +            if (cache) {
 | 
	
		
			
				|  |  | +                //console.log('cache');
 | 
	
		
			
				|  |  | +                return cache;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            //console.log('build');
 | 
	
		
			
				|  |  |              if (!this.stageImData) {
 | 
	
		
			
				|  |  |                  this.stageImData = {};
 | 
	
		
			
				|  |  |                  try {
 | 
	
	
		
			
				|  | @@ -141,6 +207,11 @@ module.exports = app => {
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          async getStageImTzBillsData(tid, sid) {
 | 
	
		
			
				|  |  | +            await this._checkTender(tid);
 | 
	
		
			
				|  |  | +            await this._checkStage(sid);
 | 
	
		
			
				|  |  | +            const cache = await this._getReportMemoryCache('mem_stage_im_tz_bills', tid, sid, this.ctx.stage.cacheTime);
 | 
	
		
			
				|  |  | +            if (cache) return cache;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |              if (!this.stageImData) {
 | 
	
		
			
				|  |  |                  this.stageImData = {};
 | 
	
		
			
				|  |  |                  try {
 | 
	
	
		
			
				|  | @@ -157,6 +228,11 @@ module.exports = app => {
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          async getStageImZlData(tid, sid) {
 | 
	
		
			
				|  |  | +            await this._checkTender(tid);
 | 
	
		
			
				|  |  | +            await this._checkStage(sid);
 | 
	
		
			
				|  |  | +            const cache = await this._getReportMemoryCache('mem_stage_im_zl', tid, sid, this.ctx.stage.cacheTime);
 | 
	
		
			
				|  |  | +            if (cache) return cache;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |              this.stageImData = {};
 | 
	
		
			
				|  |  |              try {
 | 
	
		
			
				|  |  |                  await this._generateStageIm(tid, sid, false);
 |