global class ConsumInventoryReportDtlArcRaesdBatch implements Database.Batchable, Database.Stateful { private Inventory_Header__c ih_new = new Inventory_Header__c(); private String docBodyRaesd = ''; private Integer docBodyRaesdBlobSize = 0; private Set defaultAssetField; private Set defaultRaesdField; private List cListAssetField = new List(); private List cListRaesdField = new List(); private Map fieldAPIName = new Map(); global List emailMessages; global Integer totalCount = 0; // 跨Batch 总件数 global Integer totalCountDone = 0; // 跨Batch 成功件数 global Integer executeCount = 0; // batch内的 总件数 global Integer executeCountDone = 0; // batch内的 成功件数 global Integer failedCount = 0; // batch内的 失败件数 global Integer fileNo; // 生成多个文件时,文件名尾部追加序号 private boolean overSizeFlg = false; // 当execute是否执行flag(false执行,true不执行) @TestVisible private static List messagesForTest; /** * 批准后, Trigger 会来调 */ global ConsumInventoryReportDtlArcRaesdBatch(Inventory_Header__c ih_new ,Integer fileNo, Integer totalCount, Integer totalCountDone) { this.emailMessages = new List(); this.ih_new = ih_new; this.fileNo = fileNo; this.totalCount = totalCount; this.totalCountDone = totalCountDone; } global Database.QueryLocator start(Database.BatchableContext bc) { bp3_Setting__c conf = bp3_Setting__c.getOrgDefaults(); if (String.isBlank(conf.InventoryDetailArchiveFolder__c)) { this.emailMessages.add('未设置CSV保存文件夹,不能开始Batch'); failedCount = 0; return Database.getQueryLocator([SELECT Id FROM Consum_Inventory_Detail__c LIMIT 0]); } Inventory_Batch_Mapping__mdt columns; List keyList; defaultAssetField = new Set(); defaultRaesdField = new Set(); columns = [select From_Columns__c, Inventory_Columns__c from Inventory_Batch_Mapping__mdt where DeveloperName = 'ConsumAsset']; keyList = columns.Inventory_Columns__c.split(','); keyList.addAll(new List { 'Inventory_Zaiku_Count_Jia__c', 'Inventory_Shipan_Count_Jia__c', //'Inventory_Weixiu_Count_Jia__c', 'Inventory_Chujie_Count_Jia__c', //'Inventory_Daibaofei_Diushi_Count_Jia__c', 'Inventory_Quantity_Jia__c', 'Inventory_Profit_Quantity__c', 'Inventory_Loss_Quantity__c', 'Inventory_Deviation_Jia__c', 'Inventory_Remarks__c', 'Barcode__c', 'Consumed_Count__c' }); for (Integer i = 0; i < keyList.size(); i++) { if (!defaultAssetField.contains(keyList[i])) { cListAssetField.add(keyList[i]); } defaultAssetField.add(keyList[i]); } String cListRaesdFieldStr = 'Consum_Apply_Name__c,Account__c,RA_Status__c,Salesdept__c,WorkPlace__c,Person_In_Charge_New__c,Person_In_Charge__c,demo_purpose2__c,RAES_No__c,RAES_Status__c,RAESD_Name__c,RAESD_Status__c,Fixture_Model_No__c,SerialNumber__c,Internal_asset_location__c,Equipment_Type__c,Salesdepartment__c,WH_location__c,Consum_Start_Date__c,Show_demonstration__c,Operation_Type__c,Case_OR_animal_organ__c,Trial_User__c,Follower_User__r.Name,Spare__c,Request_approval_time__c,Select_Time__c,Request_shipping_day__c,Shipment_request_time2__c,Shippment_loaner_time__c,Loaner_received_time__c,Asset_return_time__c,Consum_Received_Day__c,Asset_Center_Confirm_Time__c,AssetManageConfirmYN__c,RAESD__c,Consum_Apply__c,Consum_Apply_Equipment_Set__c,Asset__c'; cListRaesdField = cListRaesdFieldStr.split(','); //获取label String objectType ='Consum_Inventory_Detail__c'; Map schemaMap = Schema.getGlobalDescribe(); Schema.SObjectType leadSchema = schemaMap.get(objectType); Map fieldMap = leadSchema.getDescribe().fields.getMap(); for(String fieldName : fieldMap.keySet()){ fieldAPIName.put(fieldName, fieldMap.get(fieldName).getDescribe().getLabel()); } Id ih_new_Id = ih_new.Id; List assetIds = new List(); String idcQuerysql = 'select ' + String.join(new List(defaultAssetField), ', ') + ' from Consum_Inventory_Detail__c' + ' where Sync_Asset_Record_Flag__c = true' + ' and Inventory_Header__c = :ih_new_Id'; for (Consum_Inventory_Detail__c idc : Database.query(idcQuerysql)) { assetIds.add(idc.Asset__c); } String querysql = 'select ' + String.join(new List(cListRaesdField), ', ') + ', Exported__c' + ' from Consum_Inventory_Detail__c where Asset__c IN :assetIds and Inventory_Header__c = :ih_new_Id and Asset_Status__c = \'出借中\'' + ' and Exported__c = false'; return Database.getQueryLocator(querysql); } global void execute(Database.BatchableContext BC, List raesdList) { // flag为true时,跳过execute处理 if (true == overSizeFlg) { //System.abortJob(BC.getJobId()); return; } executeCount += raesdList.size(); Integer DOC_MAX_SIZE = Integer.valueOf(System.Label.DocumentFileSize_KB.replaceAll(',', '')) * 1000; // 1024 ? 1000 ? 暂时用 1000 了 Savepoint sp = Database.setSavepoint(); try { List inventoryLst = new List(); String executeBodyRaesd = ''; // 借出明细 for (Integer i = 0; i < raesdList.size(); i++) { Consum_Inventory_Detail__c dl = raesdList[i]; String dummyRaesd = setDocBodyObject(dl, 'Caesd', false); Integer dummyRaesdSize = Blob.valueOf(dummyRaesd).size(); // 当前字符串大小未超过 DOC_MAX_SIZE if (dummyRaesdSize <= DOC_MAX_SIZE - docBodyRaesdBlobSize) { executeCountDone++; executeBodyRaesd += dummyRaesd; docBodyRaesdBlobSize += dummyRaesdSize; // 处理完毕对象Exported字段置true dl.Exported__c = true; inventoryLst.add(dl); } // 当前字符串大小超过 DOC_MAX_SIZE else { overSizeFlg = true; if (0 == fileNo) { fileNo++; } executeCount = executeCount - raesdList.size(); executeCount = executeCount + i; break; } } docBodyRaesd += executeBodyRaesd; system.debug('###executeCount : ' + executeCount); system.debug('###executeCountDone : ' + executeCountDone); // 处理完毕对象update update inventoryLst; } catch (Exception e) { Database.rollback(sp); failedCount += raesdList.size(); System.debug(LoggingLevel.ERROR, e.getMessage() + '\n' + e.getStackTraceString()); this.emailMessages.add(e.getMessage() + '\n' + e.getStackTraceString()); } } private String setDocBodyObject(sObject obj, String typeStr, boolean label) { List toCol; if (typeStr == 'Caesd') { toCol = cListRaesdField; } Schema.SObjectType targetType = Consum_Inventory_Detail__c.sObjectType; Schema.DescribeSObjectResult sobjResult = targetType.getDescribe(); Map m = sobjResult.fields.getMap(); String docBody = ''; for (Integer i = 0; i < toCol.size(); i++) { List cols = toCol[i].split('\\.'); if (obj == null) { if (label) { docBody += fieldAPIName.get(toCol[i].toLowerCase().replace('__r.name','__c')).escapeCsv(); } else { docBody += toCol[i].escapeCsv(); } } else { Object val = CreateRelationListPagingCmpCtrl.getObjectValue(obj, cols); if (val != null) { Schema.DescribeFieldResult descField = m.get(toCol[i].toLowerCase().replace('__r.name','__c')).getDescribe(); if(descField.getType() == DisplayType.DATETIME && String.isNotBlank('' + val)){ docBody += (''+((Datetime) val).addHours(8)).escapeCsv(); } else{ docBody += ('' + val).escapeCsv(); } } } // 加 , 和 行末 的换行 if (i < toCol.size() - 1) { docBody += ','; } else { docBody += '\r\n'; } } return docBody; } public Document getArchiveFilenameForInsert(String typeStr, Inventory_Header__c ih_new, Integer fileNo) { bp3_Setting__c conf = bp3_Setting__c.getOrgDefaults(); Id folderId = conf.InventoryDetailArchiveFolder__c; String fileNoStr = ''; if (fileNo != 0) { fileNoStr = '_' + String.valueOf(fileNo); } String filename = ih_new.Inventory_Start_Date__c.year() + ('' + ih_new.Inventory_Start_Date__c.month()).leftPad(2, '0') + '_' + ih_new.Internal_asset_location__c + '_耗材借出明细' + fileNoStr + '.csv'; Document ret = new Document(); ret.Name = filename; ret.FolderId = folderId; ret.Type = 'csv'; insert ret; String docBody = setDocBodyObject(null, typeStr, false); docBody += setDocBodyObject(null, typeStr, true); ret.Body = Blob.valueOf(docBody); return ret; } global void finish(Database.BatchableContext BC) { if (docBodyRaesd == '') { this.emailMessages.add('耗材明细数据不存在'); } else { if (this.emailMessages.size() == 0 && executeCount == executeCountDone) { Document raesdDoc = getArchiveFilenameForInsert('Caesd', ih_new, fileNo); raesdDoc.Body = Blob.valueOf(raesdDoc.Body.toString() + docBodyRaesd); Database.SaveResult saveResult = Database.update(raesdDoc, false); if (!saveResult.isSuccess()) { failedCount = executeCountDone; // Docuemnt 都算成 fail this.emailMessages = FixtureUtil.setSaveError(new List{saveResult}, Document.sObjectType, new List{raesdDoc}, this.emailMessages); } } totalCount += executeCount; totalCountDone += executeCountDone; // flag为true(字符串大小超过9.9M),且未发生错误时再次执行batch if (true == overSizeFlg && this.emailMessages.size() == 0 && executeCount == executeCountDone) { fileNo++; ConsumInventoryReportDtlArcRaesdBatch raesdBatch = new ConsumInventoryReportDtlArcRaesdBatch(ih_new, fileNo, totalCount, totalCountDone); Database.executeBatch(raesdBatch); } else { // TODO wwf email ConsumInventoryReportDtlArcRaesdBatch.messagesForTest = this.emailMessages; // 发 mail BatchEmailUtil be = new BatchEmailUtil(); String[] toList = new String[]{}; String title = '存档盘点CSV 耗材盘点明细(借出中) #' + fileNo + ' '; if (false == overSizeFlg) { title += '最后 '; } String[] ccList = new String[]{}; Inventory_Header__c iheader = [select Id, Inventory_Status__c, Name, Internal_asset_location__c, Inventory_Start_Date__c from Inventory_Header__c where Id = :ih_new.Id]; String text = '耗材盘点报告书编号:' + iheader.Name + ' 盘点地点:' + iheader.Internal_asset_location__c + ' 盘点开始日:' + iheader.Inventory_Start_Date__c; if(this.emailMessages.size() == 0 && totalCount == totalCountDone) { toList.add(UserInfo.getUserEmail()); be.successMail(toList, ccList, title, totalCountDone, text); } else { String emailLabel = 'BatchNotify'; for (OrgWideEmailAddress tmpEmailObj : [SELECT Id, Address, DisplayName FROM OrgWideEmailAddress WHERE DisplayName like :emailLabel]) { ccList.add(tmpEmailObj.Address); } for(String email : System.Label.Inventory_Result_Email.split(',')){ ccList.add(email); } be.failedMail(toList, ccList, title, String.join(this.emailMessages, '\n'), totalCount, totalCountDone, failedCount, text); } be.send(); } } } }