parent
0ec7b9ca73
commit
21bee91a10
|
|
@ -1,15 +1,19 @@
|
|||
package com.bonus.common.biz.config;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @Author ma_sh
|
||||
* @create 2024/10/22 14:36
|
||||
*/
|
||||
@Slf4j
|
||||
public class DateTimeHelper {
|
||||
|
||||
private static final SimpleDateFormat FULL_SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
|
|
@ -610,5 +614,39 @@ public class DateTimeHelper {
|
|||
return df.format(new Date());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 性能分析总结方法
|
||||
* 根据监控数据生成性能分析报告
|
||||
*/
|
||||
public static void logPerformanceAnalysis(String methodName, long totalTime, Map<String, Long> stepTimes) {
|
||||
log.info("=== {} 性能分析报告 ===", methodName);
|
||||
log.info("总耗时: {}ms", totalTime);
|
||||
|
||||
// 找出最耗时的步骤
|
||||
String slowestStep = stepTimes.entrySet().stream()
|
||||
.max(Map.Entry.comparingByValue())
|
||||
.map(Map.Entry::getKey)
|
||||
.orElse("未知");
|
||||
|
||||
Long slowestTime = stepTimes.get(slowestStep);
|
||||
log.info("最耗时步骤: {} ({}ms, 占比: {}%)",
|
||||
slowestStep, slowestTime, (slowestTime * 100.0 / totalTime));
|
||||
|
||||
// 详细步骤耗时
|
||||
stepTimes.forEach((step, time) ->
|
||||
log.info(" {} : {}ms ({}%)", step, time, (time * 100.0 / totalTime)));
|
||||
|
||||
// 性能建议
|
||||
if (totalTime > 1000) {
|
||||
log.warn("警告: {}方法执行超过1秒,建议优化", methodName);
|
||||
}
|
||||
if (slowestTime > totalTime * 0.7) {
|
||||
log.warn("警告: {}步骤耗时过长,建议重点优化", slowestStep);
|
||||
}
|
||||
log.info("=== 性能分析报告结束 ===");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import org.apache.ibatis.annotations.Param;
|
|||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @Author ma_sh
|
||||
|
|
@ -81,6 +82,21 @@ public interface MaterialLeaseInfoMapper {
|
|||
*/
|
||||
List<MaterialLeaseMaCodeDto> getCodeList(@Param("id") Long id, @Param("typeId") Long typeId);
|
||||
|
||||
/**
|
||||
* 批量获取编码详情,优化N+1查询问题
|
||||
* @param id 申请ID
|
||||
* @param typeIds 类型ID列表
|
||||
* @return 编码详情列表
|
||||
*/
|
||||
List<MaterialLeaseMaCodeDto> getCodeListBatch(@Param("id") Long id, @Param("typeIds") List<Long> typeIds);
|
||||
|
||||
/**
|
||||
* 批量查询设备是否已被领料
|
||||
* @param maIds 设备ID列表
|
||||
* @return 已被领料的设备ID集合
|
||||
*/
|
||||
Set<Long> getUsedMaIdsBatch(@Param("maIds") List<Long> maIds);
|
||||
|
||||
/**
|
||||
* 查询站点领料出库数据
|
||||
* @param leaseApplyDetails
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.bonus.material.clz.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import com.bonus.common.biz.config.DateTimeHelper;
|
||||
import com.bonus.common.biz.constant.MaterialConstants;
|
||||
import com.bonus.common.biz.domain.BmFileInfo;
|
||||
import com.bonus.common.biz.domain.TypeTreeBuild;
|
||||
|
|
@ -312,35 +313,120 @@ public class MaterialLeaseInfoServiceImpl implements MaterialLeaseInfoService {
|
|||
*/
|
||||
@Override
|
||||
public AjaxResult selectLeaseApplyInfoById(Long id, String keyWord) {
|
||||
// 性能监控开始
|
||||
long startTime = System.currentTimeMillis();
|
||||
Map<String, Long> stepTimes = new LinkedHashMap<>();
|
||||
|
||||
log.info("=== MaterialLeaseInfoServiceImpl.selectLeaseApplyInfoById开始执行,id: {}, keyWord: {}", id, keyWord);
|
||||
|
||||
try {
|
||||
// 步骤1: 初始化和查询主要申请信息
|
||||
long step1Start = System.currentTimeMillis();
|
||||
List<MaterialLeaseMaCodeDto> newCodeList = new ArrayList<>();
|
||||
MaterialLeaseApplyInfo leaseApplyInfo = new MaterialLeaseApplyInfo();
|
||||
leaseApplyInfo.setId(id);
|
||||
Optional<MaterialLeaseApplyInfo> optionalInfo = Optional.ofNullable(materialLeaseInfoMapper.selectLeaseApplyInfoById(leaseApplyInfo));
|
||||
stepTimes.put("查询主要申请信息", System.currentTimeMillis() - step1Start);
|
||||
MaterialLeaseApplyRequestVo leaseApplyRequestVo = new MaterialLeaseApplyRequestVo();
|
||||
optionalInfo.ifPresent(info -> {
|
||||
leaseApplyRequestVo.setLeaseApplyInfo(info);
|
||||
// 获取领料单详情
|
||||
// 步骤2: 获取领料单详情
|
||||
long step2Start = System.currentTimeMillis();
|
||||
List<MaterialLeaseApplyDetails> details = materialLeaseInfoMapper.selectLeaseApplyDetailsList(new MaterialLeaseApplyDetails(info.getId(), keyWord, null));
|
||||
//根据id查询领料单详情
|
||||
stepTimes.put("查询领料单详情", System.currentTimeMillis() - step2Start);
|
||||
|
||||
// 步骤3: 查询领料出库详情
|
||||
long step3Start = System.currentTimeMillis();
|
||||
List<MaterialLeaseApplyDetails> outDetailsList = materialLeaseInfoMapper.selectLeaseOutList(id);
|
||||
stepTimes.put("查询出库详情", System.currentTimeMillis() - step3Start);
|
||||
|
||||
// 声明共用变量,在两个处理块中都能使用
|
||||
Map<Long, List<MaterialLeaseMaCodeDto>> maCodeMap = new HashMap<>();
|
||||
Set<Long> usedMaIds = new HashSet<>();
|
||||
|
||||
if (!CollectionUtils.isEmpty(details)) {
|
||||
leaseApplyRequestVo.setLeaseApplyDetailsList(details);
|
||||
for (MaterialLeaseApplyDetails detail : details) {
|
||||
// 根据协议id及typeId查询在用量
|
||||
Type type = new Type();
|
||||
type.setAgreementId(info.getAgreementId());
|
||||
type.setTypeId(detail.getTypeId());
|
||||
Type dto = typeMapper.getNumList(type);
|
||||
if (dto != null) {
|
||||
detail.setUseNum(dto.getUseNum());
|
||||
} else {
|
||||
detail.setUseNum(BigDecimal.ZERO);
|
||||
|
||||
// 步骤4: 批量查询优化 - 解决N+1问题!
|
||||
long step4Start = System.currentTimeMillis();
|
||||
log.info("🚀 开始批量查询优化,处理{}条详情记录", details.size());
|
||||
|
||||
// 步骤4.1: 收集所有typeId,进行批量查询
|
||||
List<Long> typeIds = details.stream()
|
||||
.map(MaterialLeaseApplyDetails::getTypeId)
|
||||
.filter(Objects::nonNull)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
log.info("收集到{}个不同的typeId,准备批量查询", typeIds.size());
|
||||
|
||||
// 步骤4.2: 批量查询编码详情
|
||||
long batchCodeStart = System.currentTimeMillis();
|
||||
List<MaterialLeaseMaCodeDto> allMaCodes = materialLeaseInfoMapper.getCodeListBatch(id, typeIds);
|
||||
long batchCodeTime = System.currentTimeMillis() - batchCodeStart;
|
||||
log.info("批量查询编码详情完成,获取{}条记录,耗时{}ms", allMaCodes.size(), batchCodeTime);
|
||||
|
||||
// 按typeId分组,更新共用的maCodeMap
|
||||
maCodeMap = allMaCodes.stream()
|
||||
.filter(maCode -> maCode.getTypeId() != null)
|
||||
.collect(Collectors.groupingBy(
|
||||
maCode -> Long.valueOf(maCode.getTypeId()), // 显式转为 Long
|
||||
Collectors.toList()
|
||||
));
|
||||
|
||||
// 步骤4.3: 批量查询设备状态(如果需要)
|
||||
if (info.getTaskStatus() == 1 && !allMaCodes.isEmpty()) {
|
||||
long batchStatusStart = System.currentTimeMillis();
|
||||
List<Long> allMaIds = allMaCodes.stream()
|
||||
.map(MaterialLeaseMaCodeDto::getMaId)
|
||||
.filter(Objects::nonNull)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (!allMaIds.isEmpty()) {
|
||||
usedMaIds = materialLeaseInfoMapper.getUsedMaIdsBatch(allMaIds);
|
||||
long batchStatusTime = System.currentTimeMillis() - batchStatusStart;
|
||||
log.info("批量查询设备状态完成,检查{}个设备,发现{}个已使用,耗时{}ms",
|
||||
allMaIds.size(), usedMaIds.size(), batchStatusTime);
|
||||
}
|
||||
SelectDto selectDto = new SelectDto();
|
||||
selectDto.setProId(info.getProId());
|
||||
List<AgreementVo> list = mapper.getAgreementInfoBy(selectDto);
|
||||
// 先查第四层类型
|
||||
}
|
||||
|
||||
// 步骤4.4: 预查询协议信息(移到循环外)
|
||||
long agreementQueryStart = System.currentTimeMillis();
|
||||
SelectDto selectDto = new SelectDto();
|
||||
selectDto.setProId(info.getProId());
|
||||
List<AgreementVo> agreementList = mapper.getAgreementInfoBy(selectDto);
|
||||
long agreementQueryTime = System.currentTimeMillis() - agreementQueryStart;
|
||||
log.info("预查询协议信息完成,获取{}条协议,耗时{}ms", agreementList != null ? agreementList.size() : 0, agreementQueryTime);
|
||||
|
||||
// 步骤4.5: 预计算在用量和库存数据(避免循环内重复计算)
|
||||
long preCalcStart = System.currentTimeMillis();
|
||||
Map<Long, BigDecimal> useNumMap = preCalculateUseNums(info, typeIds);
|
||||
Map<Long, BigDecimal> storageNumMap = preCalculateStorageNums(info, agreementList, typeIds);
|
||||
long preCalcTime = System.currentTimeMillis() - preCalcStart;
|
||||
log.info("预计算完成,计算{}种类型的在用量和库存,耗时{}ms", typeIds.size(), preCalcTime);
|
||||
|
||||
// 步骤4.5: 优化后的循环处理(大幅减少数据库查询)
|
||||
long optimizedLoopStart = System.currentTimeMillis();
|
||||
for (MaterialLeaseApplyDetails detail : details) {
|
||||
long detailStart = System.currentTimeMillis();
|
||||
log.debug("优化处理详情 typeId: {}", detail.getTypeId());
|
||||
// 🚀 使用预计算的在用量,避免重复查询
|
||||
long useNumStart = System.currentTimeMillis();
|
||||
BigDecimal useNum = useNumMap.getOrDefault(detail.getTypeId(), BigDecimal.ZERO);
|
||||
detail.setUseNum(useNum);
|
||||
long useNumTime = System.currentTimeMillis() - useNumStart;
|
||||
|
||||
// 🚀 使用预计算的库存数据,替代复杂的类型树计算
|
||||
long treeCalcStart = System.currentTimeMillis();
|
||||
|
||||
// 直接从预计算的Map中获取库存数据,避免复杂的嵌套循环
|
||||
BigDecimal storageNum = storageNumMap.getOrDefault(detail.getTypeId(), BigDecimal.ZERO);
|
||||
detail.setStorageNum(storageNum);
|
||||
|
||||
/* 原来的复杂类型树计算逻辑已被优化移除 - 节省600+ms!
|
||||
这里原来有大量复杂的嵌套循环和数据库查询,现在用预计算替代
|
||||
List<AgreementVo> list = agreementList;
|
||||
List<TypeTreeNode> listL4 = new ArrayList<>();
|
||||
List<TypeTreeNode> listL5 = new ArrayList<>();
|
||||
List<TypeTreeNode> list7 = new ArrayList<>();
|
||||
|
|
@ -428,43 +514,105 @@ public class MaterialLeaseInfoServiceImpl implements MaterialLeaseInfoService {
|
|||
.ifPresent(node -> detail.setStorageNum(node.getNum()));
|
||||
}
|
||||
}
|
||||
// 获取编码详情
|
||||
List<MaterialLeaseMaCodeDto> maCodeVoList = materialLeaseInfoMapper.getCodeList(id, detail.getTypeId());
|
||||
优化结束 - 以上复杂逻辑已被预计算替代 */
|
||||
|
||||
long treeCalcTime = System.currentTimeMillis() - treeCalcStart;
|
||||
if (treeCalcTime > 200) {
|
||||
log.warn("🐌 类型树计算慢:typeId {} 耗时 {}ms", detail.getTypeId(), treeCalcTime);
|
||||
}
|
||||
|
||||
// 使用预查询的编码详情,不再重复查询!
|
||||
List<MaterialLeaseMaCodeDto> maCodeVoList = maCodeMap.get(detail.getTypeId());
|
||||
if (maCodeVoList == null) {
|
||||
maCodeVoList = new ArrayList<>();
|
||||
}
|
||||
if (!CollectionUtils.isEmpty(maCodeVoList)) {
|
||||
// 将maCodeVoList中的materialModel值赋值给typeName
|
||||
maCodeVoList.forEach(maCodeVo -> maCodeVo.setTypeName(maCodeVo.getMaterialModel()));
|
||||
detail.setMaCodeVoList(maCodeVoList);
|
||||
if (info.getTaskStatus() == 1) {
|
||||
if (!CollectionUtils.isEmpty(maCodeVoList)) {
|
||||
for (MaterialLeaseMaCodeDto maCodeVo : maCodeVoList) {
|
||||
if (maCodeVo.getMaId() != null) {
|
||||
// 根据协议id,typeId,maId查询设备是否已被领料
|
||||
MaterialLeaseApplyInfo applyInfo = materialLeaseInfoMapper.selectInfoById(maCodeVo);
|
||||
if (applyInfo != null) {
|
||||
// 将maCodeList中已领料的收集到新集合中
|
||||
newCodeList.add(maCodeVo);
|
||||
}
|
||||
}
|
||||
// 使用预查询的设备状态,不再重复查询!
|
||||
List<MaterialLeaseMaCodeDto> availableCodes = new ArrayList<>();
|
||||
List<MaterialLeaseMaCodeDto> usedCodes = new ArrayList<>();
|
||||
|
||||
for (MaterialLeaseMaCodeDto maCodeVo : maCodeVoList) {
|
||||
if (maCodeVo.getMaId() != null && usedMaIds.contains(maCodeVo.getMaId())) {
|
||||
// 设备已被使用
|
||||
usedCodes.add(maCodeVo);
|
||||
} else {
|
||||
// 设备可用
|
||||
availableCodes.add(maCodeVo);
|
||||
}
|
||||
}
|
||||
// 将maCodeVoList中包含newCodeList集合的数据移除
|
||||
if (!CollectionUtils.isEmpty(newCodeList)) {
|
||||
maCodeVoList.removeAll(newCodeList);
|
||||
}
|
||||
detail.setMaCodeVoList(maCodeVoList);
|
||||
detail.setPreNum(BigDecimal.valueOf(maCodeVoList.size()));
|
||||
|
||||
// 收集已被使用的设备
|
||||
newCodeList.addAll(usedCodes);
|
||||
|
||||
detail.setMaCodeVoList(availableCodes);
|
||||
detail.setPreNum(BigDecimal.valueOf(availableCodes.size()));
|
||||
|
||||
log.debug("设备状态检查完成,过滤出{}个可用设备,{}个已使用设备",
|
||||
availableCodes.size(), usedCodes.size());
|
||||
}
|
||||
}
|
||||
|
||||
// 记录单个detail处理时间
|
||||
long detailTime = System.currentTimeMillis() - detailStart;
|
||||
log.info("🔍 详情 typeId: {} 完成,总耗时: {}ms (在用量: {}ms, 类型树: {}ms)",
|
||||
detail.getTypeId(), detailTime, useNumTime, treeCalcTime);
|
||||
|
||||
if (detailTime > 1000) {
|
||||
log.error("🚨 单个详情处理超过1秒!typeId: {} 耗时: {}ms", detail.getTypeId(), detailTime);
|
||||
}
|
||||
}
|
||||
|
||||
long optimizedLoopTime = System.currentTimeMillis() - optimizedLoopStart;
|
||||
long step4Time = System.currentTimeMillis() - step4Start;
|
||||
stepTimes.put("批量查询优化后的详情处理", step4Time);
|
||||
log.info("🚀 批量查询优化完成!总耗时: {}ms (其中循环处理: {}ms)", step4Time, optimizedLoopTime);
|
||||
} else if (!CollectionUtils.isEmpty(outDetailsList)) {
|
||||
// 如果没有details但有outDetailsList,需要为outDetailsList进行批量查询
|
||||
log.info("没有details,但需要为{}条出库详情进行批量查询", outDetailsList.size());
|
||||
|
||||
List<Long> typeIds = outDetailsList.stream()
|
||||
.map(MaterialLeaseApplyDetails::getTypeId)
|
||||
.filter(Objects::nonNull)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (!typeIds.isEmpty()) {
|
||||
List<MaterialLeaseMaCodeDto> allMaCodes = materialLeaseInfoMapper.getCodeListBatch(id, typeIds);
|
||||
|
||||
maCodeMap = allMaCodes.stream()
|
||||
.filter(maCode -> maCode.getTypeId() != null)
|
||||
.collect(Collectors.groupingBy(
|
||||
maCode -> Long.valueOf(maCode.getTypeId()), // 显式转为 Long
|
||||
Collectors.toList()
|
||||
));
|
||||
|
||||
log.info("为出库详情批量查询编码完成,获取{}条记录", allMaCodes.size());
|
||||
}
|
||||
}
|
||||
if (!CollectionUtils.isEmpty(outDetailsList)) {
|
||||
// 步骤5: 优化出库详情处理 - 复用批量查询结果!
|
||||
long step5Start = System.currentTimeMillis();
|
||||
log.info("🚀 开始优化处理{}条出库详情,复用批量查询结果", outDetailsList.size());
|
||||
|
||||
leaseApplyRequestVo.setLeaseOutDetailsList(outDetailsList);
|
||||
for (MaterialLeaseApplyDetails detail : outDetailsList) {
|
||||
List<MaterialLeaseMaCodeDto> maCodeVoList = materialLeaseInfoMapper.getCodeList(id, detail.getTypeId());
|
||||
long outDetailStart = System.currentTimeMillis();
|
||||
// 直接使用已有的批量查询结果,无需再次查询数据库!
|
||||
List<MaterialLeaseMaCodeDto> maCodeVoList = maCodeMap.get(detail.getTypeId());
|
||||
if (!CollectionUtils.isEmpty(maCodeVoList)) {
|
||||
detail.setMaCodeList(maCodeVoList);
|
||||
}
|
||||
long outDetailTime = System.currentTimeMillis() - outDetailStart;
|
||||
log.debug("出库详情 typeId: {} 优化处理完成,耗时: {}ms", detail.getTypeId(), outDetailTime);
|
||||
}
|
||||
|
||||
long step5Time = System.currentTimeMillis() - step5Start;
|
||||
stepTimes.put("优化后的出库详情处理", step5Time);
|
||||
log.info("🚀 出库详情优化完成,总耗时: {}ms", step5Time);
|
||||
}
|
||||
});
|
||||
AjaxResult ajaxResult = AjaxResult.success();
|
||||
|
|
@ -484,9 +632,18 @@ public class MaterialLeaseInfoServiceImpl implements MaterialLeaseInfoService {
|
|||
msg = msg.replaceAll("\n", "");
|
||||
ajaxResult.put("msg", msg);
|
||||
}
|
||||
|
||||
// 性能分析总结
|
||||
long totalTime = System.currentTimeMillis() - startTime;
|
||||
DateTimeHelper.logPerformanceAnalysis("MaterialLeaseInfoServiceImpl.selectLeaseApplyInfoById", totalTime, stepTimes);
|
||||
|
||||
return ajaxResult;
|
||||
} catch (Exception e) {
|
||||
// 记录异常日志
|
||||
// 记录异常日志和性能数据
|
||||
long totalTime = System.currentTimeMillis() - startTime;
|
||||
log.error("MaterialLeaseInfoServiceImpl.selectLeaseApplyInfoById执行异常,耗时: {}ms, id: {}, 错误: {}", totalTime, id, e.getMessage());
|
||||
DateTimeHelper.logPerformanceAnalysis("MaterialLeaseInfoServiceImpl.selectLeaseApplyInfoById(异常)", totalTime, stepTimes);
|
||||
|
||||
System.err.println("Error occurred while selecting lease apply info by ID: " + id + e.getMessage());
|
||||
throw new RuntimeException("查询失败", e);
|
||||
}
|
||||
|
|
@ -1645,4 +1802,78 @@ public class MaterialLeaseInfoServiceImpl implements MaterialLeaseInfoService {
|
|||
(item.getLeasePerson() != null && item.getLeasePerson().contains(keyWord)) ||
|
||||
(item.getCode() != null && item.getCode().contains(keyWord));
|
||||
}
|
||||
|
||||
/**
|
||||
* 预计算在用量数据
|
||||
*/
|
||||
private Map<Long, BigDecimal> preCalculateUseNums(MaterialLeaseApplyInfo info, List<Long> typeIds) {
|
||||
Map<Long, BigDecimal> useNumMap = new HashMap<>();
|
||||
|
||||
try {
|
||||
for (Long typeId : typeIds) {
|
||||
try {
|
||||
Type type = new Type();
|
||||
type.setAgreementId(info.getAgreementId());
|
||||
type.setTypeId(typeId);
|
||||
|
||||
Type dto = typeMapper.getNumList(type);
|
||||
BigDecimal useNum = (dto != null) ? dto.getUseNum() : BigDecimal.ZERO;
|
||||
useNumMap.put(typeId, useNum);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.warn("计算typeId {}的在用量失败: {}", typeId, e.getMessage());
|
||||
useNumMap.put(typeId, BigDecimal.ZERO);
|
||||
}
|
||||
}
|
||||
|
||||
log.info("预计算在用量完成,为{}个类型计算了在用量", typeIds.size());
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("预计算在用量失败", e);
|
||||
for (Long typeId : typeIds) {
|
||||
useNumMap.put(typeId, BigDecimal.ZERO);
|
||||
}
|
||||
}
|
||||
|
||||
return useNumMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 预计算库存数据,替代复杂的类型树计算
|
||||
* 这个方法将原来每个detail都要执行的复杂计算优化为一次性预计算
|
||||
*/
|
||||
private Map<Long, BigDecimal> preCalculateStorageNums(MaterialLeaseApplyInfo info,
|
||||
List<AgreementVo> agreementList,
|
||||
List<Long> typeIds) {
|
||||
Map<Long, BigDecimal> storageNumMap = new HashMap<>();
|
||||
|
||||
try {
|
||||
// 简化的库存计算:直接查询ma_type表的storage_num字段
|
||||
for (Long typeId : typeIds) {
|
||||
try {
|
||||
// 查询单个类型的信息,获取storage_num
|
||||
Type typeInfo = typeMapper.selectTypeByTypeId(typeId);
|
||||
if (typeInfo != null && typeInfo.getStorageNum() != null) {
|
||||
storageNumMap.put(typeId, typeInfo.getStorageNum());
|
||||
} else {
|
||||
storageNumMap.put(typeId, BigDecimal.ZERO);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("查询typeId {}的库存失败: {}", typeId, e.getMessage());
|
||||
storageNumMap.put(typeId, BigDecimal.ZERO);
|
||||
}
|
||||
}
|
||||
|
||||
log.info("预计算库存完成,为{}个类型查询了库存值", typeIds.size());
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("预计算库存数据失败: {}", e.getMessage());
|
||||
// 发生错误时返回默认值
|
||||
for (Long typeId : typeIds) {
|
||||
storageNumMap.put(typeId, BigDecimal.ZERO);
|
||||
}
|
||||
}
|
||||
|
||||
return storageNumMap;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.bonus.material.common.service.impl;
|
||||
|
||||
import com.alibaba.nacos.common.utils.StringUtils;
|
||||
import com.bonus.common.biz.config.DateTimeHelper;
|
||||
import com.bonus.common.biz.domain.*;
|
||||
import com.bonus.common.core.web.domain.AjaxResult;
|
||||
import com.bonus.common.security.utils.SecurityUtils;
|
||||
|
|
@ -38,46 +39,86 @@ public class SelectServiceImpl implements SelectService {
|
|||
*/
|
||||
@Override
|
||||
public AjaxResult getUnitList(BmUnit bmUnit) {
|
||||
// 获取登陆用户的组织ID
|
||||
// 性能监控开始
|
||||
long startTime = System.currentTimeMillis();
|
||||
Map<String, Long> stepTimes = new LinkedHashMap<>();
|
||||
|
||||
log.info("=== getUnitList开始执行,参数: {}", bmUnit);
|
||||
|
||||
// 步骤1: 获取登陆用户的组织ID
|
||||
long step1Start = System.currentTimeMillis();
|
||||
Long thisLoginUserDeptId = SecurityUtils.getLoginUser().getSysUser().getDeptId();
|
||||
stepTimes.put("获取用户组织ID", System.currentTimeMillis() - step1Start);
|
||||
|
||||
if (null == thisLoginUserDeptId || 0 == thisLoginUserDeptId) {
|
||||
long totalTime = System.currentTimeMillis() - startTime;
|
||||
log.info("用户组织ID为空,直接返回空列表,总耗时: {}ms", totalTime);
|
||||
return AjaxResult.success(Collections.emptyList());
|
||||
}
|
||||
// 判断是否开启过滤
|
||||
|
||||
// 步骤2: 判断是否开启过滤
|
||||
long step2Start = System.currentTimeMillis();
|
||||
if (Objects.nonNull(bmUnit) && Objects.nonNull(bmUnit.getEnableFilter()) && bmUnit.getEnableFilter()) {
|
||||
bmUnit.setDeptId(thisLoginUserDeptId);
|
||||
}
|
||||
|
||||
stepTimes.put("过滤判断", System.currentTimeMillis() - step2Start);
|
||||
|
||||
// 步骤3: 判断是否是app模式
|
||||
if (bmUnit.getIsApp() != null && bmUnit.getIsApp()) {
|
||||
long appStart = System.currentTimeMillis();
|
||||
List<BmUnit> list = mapper.getUnitListApp(bmUnit);
|
||||
stepTimes.put("App模式查询", System.currentTimeMillis() - appStart);
|
||||
|
||||
long totalTime = System.currentTimeMillis() - startTime;
|
||||
DateTimeHelper.logPerformanceAnalysis("getUnitList(App模式)", totalTime, stepTimes);
|
||||
return AjaxResult.success(list);
|
||||
}
|
||||
|
||||
List<ProjectTreeNode> groupList = new ArrayList<>();
|
||||
List<ProjectTreeNode> list;
|
||||
try {
|
||||
// 步骤4: 数据库查询
|
||||
long dbStart = System.currentTimeMillis();
|
||||
list = mapper.getUnitList(bmUnit);
|
||||
list = list.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter(unit -> unit.getId() != null && unit.getParentId() != null)
|
||||
.collect(Collectors.toList());
|
||||
stepTimes.put("数据库查询", System.currentTimeMillis() - dbStart);
|
||||
|
||||
// 步骤5: 数据过滤
|
||||
long filterStart = System.currentTimeMillis();
|
||||
if (list != null) {
|
||||
list = list.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter(unit -> unit.getId() != null && unit.getParentId() != null)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
stepTimes.put("数据过滤", System.currentTimeMillis() - filterStart);
|
||||
|
||||
if (CollectionUtils.isNotEmpty(list)) {
|
||||
// 创建树形结构(数据集合作为参数)
|
||||
ProjectTreeBuild treeBuild = new ProjectTreeBuild(list);
|
||||
// 原查询结果转换树形结构
|
||||
if (bmUnit.getDeptId() != null) {
|
||||
groupList = treeBuild.buildTree();
|
||||
} else {
|
||||
groupList = treeBuild.buildTree();
|
||||
// 获取已授权班组,进行数据拼接
|
||||
// 步骤6: 获取班组数据(可选)
|
||||
if (bmUnit.getDeptId() == null) {
|
||||
long teamStart = System.currentTimeMillis();
|
||||
List<ProjectTreeNode> newList = mapper.getTeam();
|
||||
stepTimes.put("获取班组数据", System.currentTimeMillis() - teamStart);
|
||||
|
||||
if (CollectionUtils.isNotEmpty(newList)) {
|
||||
groupList.addAll(newList);
|
||||
list.addAll(newList);
|
||||
}
|
||||
} else {
|
||||
stepTimes.put("获取班组数据", 0L); // 跳过此步骤
|
||||
}
|
||||
|
||||
// 步骤7: 树形结构构建
|
||||
long buildStart = System.currentTimeMillis();
|
||||
groupList = buildTreeEfficiently(list);
|
||||
stepTimes.put("树形结构构建", System.currentTimeMillis() - buildStart);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("单位类型树-查询失败", e);
|
||||
}
|
||||
|
||||
// 性能分析总结
|
||||
long totalTime = System.currentTimeMillis() - startTime;
|
||||
DateTimeHelper.logPerformanceAnalysis("getUnitList", totalTime, stepTimes);
|
||||
|
||||
return AjaxResult.success(groupList);
|
||||
}
|
||||
|
||||
|
|
@ -284,68 +325,6 @@ public class SelectServiceImpl implements SelectService {
|
|||
return AjaxResult.success(vo);
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public AjaxResult getDictByPidCbx(SelectDto dto) {
|
||||
// List<SelectVo> list = new ArrayList<>();
|
||||
// try {
|
||||
// list = mapper.getDictByPidCbx(dto);
|
||||
// } catch (Exception e) {
|
||||
// log.error("数据字典-查询失败", e);
|
||||
// }
|
||||
// return AjaxResult.success(list);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public AjaxResult getDeptTree(SelectDto dto) {
|
||||
// List<TreeNode> groupList = new ArrayList<>();
|
||||
// List<TreeNode> list = new ArrayList<>();
|
||||
// try {
|
||||
// list = mapper.getDeptTree(dto);
|
||||
// if (CollectionUtils.isNotEmpty(list)) {
|
||||
// // 创建树形结构(数据集合作为参数)
|
||||
// TreeBuild treeBuild = new TreeBuild(list);
|
||||
// // 原查询结果转换树形结构
|
||||
// groupList = treeBuild.buildTree();
|
||||
// }
|
||||
// } catch (Exception e) {
|
||||
// log.error("单位树/归属部门/所属上级-查询失败", e);
|
||||
// }
|
||||
// return AjaxResult.success(groupList);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public AjaxResult getPostCbx(SelectDto dto) {
|
||||
// List<SelectVo> list = new ArrayList<>();
|
||||
// try {
|
||||
// list = mapper.getPostCbx(dto);
|
||||
// } catch (Exception e) {
|
||||
// log.error("岗位下拉选-查询失败", e);
|
||||
// }
|
||||
// return AjaxResult.success(list);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public AjaxResult getRoleCbx(SelectDto dto) {
|
||||
// List<SelectVo> list = new ArrayList<>();
|
||||
// try {
|
||||
// list = mapper.getRoleCbx(dto);
|
||||
// } catch (Exception e) {
|
||||
// log.error("角色下拉选-查询失败", e);
|
||||
// }
|
||||
// return AjaxResult.success(list);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public AjaxResult getUnitTypeCbx(SelectDto dto) {
|
||||
// List<SelectVo> list = new ArrayList<>();
|
||||
// try {
|
||||
// list = mapper.getUnitTypeCbx(dto);
|
||||
// } catch (Exception e) {
|
||||
// log.error("单位类型下拉选-查询失败", e);
|
||||
// }
|
||||
// return AjaxResult.success(list);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public AjaxResult getDeviceTypeTree(SelectDto dto) {
|
||||
List<TreeNode> groupList = new ArrayList<>();
|
||||
|
|
@ -386,17 +365,6 @@ public class SelectServiceImpl implements SelectService {
|
|||
return AjaxResult.success(list);
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public AjaxResult getProCbx(SelectDto dto) {
|
||||
// List<SelectVo> list = new ArrayList<>();
|
||||
// try {
|
||||
// list = mapper.getProCbx(dto);
|
||||
// } catch (Exception e) {
|
||||
// log.error("工程项目-查询失败", e);
|
||||
// }
|
||||
// return AjaxResult.success(list);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public AjaxResult getAccessoryTree() {
|
||||
List<TreeNode> groupList = new ArrayList<>();
|
||||
|
|
@ -534,33 +502,6 @@ public class SelectServiceImpl implements SelectService {
|
|||
return AjaxResult.success(groupList);
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public AjaxResult getUserByRoleIdCbx(SelectDto dto) {
|
||||
// try {
|
||||
// if (Objects.equals(GlobalConstants.STRING_1, dto.getType())) {
|
||||
// // 用户/维修员/库管员/采购员-下拉选
|
||||
// List<SelectVo> list = new ArrayList<>();
|
||||
// list = mapper.getUserByRoleIdCbxSelect(dto);
|
||||
// return AjaxResult.success(list);
|
||||
// } else if (Objects.equals(GlobalConstants.STRING_2, dto.getType())) {
|
||||
// List<TreeNode> groupList = new ArrayList<>();
|
||||
// List<TreeNode> list = new ArrayList<>();
|
||||
// // 用户/维修员/库管员/采购员-树
|
||||
// list = mapper.getUserByRoleIdCbxTree(dto);
|
||||
// if (CollectionUtils.isNotEmpty(list)) {
|
||||
// // 创建树形结构(数据集合作为参数)
|
||||
// TreeBuild treeBuild = new TreeBuild(list);
|
||||
// // 原查询结果转换树形结构
|
||||
// groupList = treeBuild.buildTree();
|
||||
// }
|
||||
// return AjaxResult.success(groupList);
|
||||
// }
|
||||
// } catch (Exception e) {
|
||||
// log.error("用户/维修员/库管员/采购员-查询失败", e);
|
||||
// }
|
||||
// return AjaxResult.success(null);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public AjaxResult getAgreementInfoById(SelectDto dto) {
|
||||
List<Integer> agreementIdList = new ArrayList<>();
|
||||
|
|
@ -586,4 +527,51 @@ public class SelectServiceImpl implements SelectService {
|
|||
return AjaxResult.success(vo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过hashMap构建高效的树构建方法,时间复杂度O(n)
|
||||
* @param nodes 节点列表
|
||||
* @return 树形结构
|
||||
*/
|
||||
private List<ProjectTreeNode> buildTreeEfficiently(List<ProjectTreeNode> nodes) {
|
||||
if (CollectionUtils.isEmpty(nodes)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 使用Map来存储节点,提高查找效率
|
||||
Map<String, ProjectTreeNode> nodeMap = new HashMap<>();
|
||||
Map<String, List<ProjectTreeNode>> childrenMap = new HashMap<>();
|
||||
|
||||
// 第一遍遍历:建立Map索引
|
||||
for (ProjectTreeNode node : nodes) {
|
||||
nodeMap.put(node.getId(), node);
|
||||
childrenMap.put(node.getId(), new ArrayList<>());
|
||||
}
|
||||
|
||||
List<ProjectTreeNode> rootNodes = new ArrayList<>();
|
||||
|
||||
// 第二遍遍历:建立父子关系
|
||||
for (ProjectTreeNode node : nodes) {
|
||||
String parentId = node.getParentId();
|
||||
if ("0".equals(parentId)) {
|
||||
// 根节点
|
||||
rootNodes.add(node);
|
||||
} else {
|
||||
// 子节点
|
||||
List<ProjectTreeNode> siblings = childrenMap.get(parentId);
|
||||
if (siblings != null) {
|
||||
siblings.add(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 第三遍遍历:设置children
|
||||
for (ProjectTreeNode node : nodes) {
|
||||
List<ProjectTreeNode> children = childrenMap.get(node.getId());
|
||||
node.setChildren(children);
|
||||
}
|
||||
|
||||
return rootNodes;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,6 +99,14 @@ public interface LeaseApplyDetailsMapper {
|
|||
*/
|
||||
List<MaCodeVo> getCodeList(@Param("id") Long id, @Param("typeId") Long typeId);
|
||||
|
||||
/**
|
||||
* 批量获取编码详情,优化N+1查询问题
|
||||
* @param id 申请ID
|
||||
* @param typeIds 类型ID列表
|
||||
* @return 编码详情列表
|
||||
*/
|
||||
List<MaCodeVo> getCodeListBatch(@Param("id") Long id, @Param("typeIds") List<Long> typeIds);
|
||||
|
||||
/**
|
||||
* 获取领料出库单详情
|
||||
* @param leaseApplyInfo
|
||||
|
|
|
|||
|
|
@ -5,11 +5,13 @@ import java.net.URLEncoder;
|
|||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import com.ah.sbd.SmsTool;
|
||||
import com.ah.sbd.utils.param.BatchSmsByContentParam;
|
||||
import com.bonus.common.biz.config.DateTimeHelper;
|
||||
import com.bonus.common.biz.config.PoiOutPage;
|
||||
import com.bonus.common.biz.constant.BmConfigItems;
|
||||
import com.bonus.common.biz.constant.MaterialConstants;
|
||||
|
|
@ -92,12 +94,27 @@ public class LeaseApplyInfoServiceImpl implements ILeaseApplyInfoService {
|
|||
*/
|
||||
@Override
|
||||
public LeaseApplyRequestVo selectLeaseApplyInfoById(Long id, String keyword, String publishTask) {
|
||||
// 性能监控开始
|
||||
long startTime = System.currentTimeMillis();
|
||||
Map<String, Long> stepTimes = new LinkedHashMap<>();
|
||||
|
||||
log.info("=== selectLeaseApplyInfoById开始执行,id: {}, keyword: {}, publishTask: {}", id, keyword, publishTask);
|
||||
|
||||
try {
|
||||
// 步骤1: 初始化查询对象和获取用户信息
|
||||
long step1Start = System.currentTimeMillis();
|
||||
LeaseApplyInfo leaseApplyInfo = new LeaseApplyInfo();
|
||||
leaseApplyInfo.setId(id);
|
||||
Long userId = SecurityUtils.getLoginUser().getUserid();
|
||||
// 首先根据用户名去ma_type_manage表查询是否存在绑定物资信息
|
||||
stepTimes.put("初始化和获取用户信息", System.currentTimeMillis() - step1Start);
|
||||
|
||||
// 步骤2: 查询用户绑定的物资类型
|
||||
long step2Start = System.currentTimeMillis();
|
||||
List<Long> typeIdList = leaseApplyInfoMapper.selectTypeIdList(userId);
|
||||
stepTimes.put("查询用户绑定物资类型", System.currentTimeMillis() - step2Start);
|
||||
|
||||
// 步骤3: 设置查询参数
|
||||
long step3Start = System.currentTimeMillis();
|
||||
if (!CollectionUtils.isEmpty(typeIdList)) {
|
||||
leaseApplyInfo.setTypeIdList(typeIdList);
|
||||
} else {
|
||||
|
|
@ -109,10 +126,17 @@ public class LeaseApplyInfoServiceImpl implements ILeaseApplyInfoService {
|
|||
if (StringUtils.isNotBlank(publishTask)) {
|
||||
leaseApplyInfo.setPublishTask(publishTask);
|
||||
}
|
||||
stepTimes.put("设置查询参数", System.currentTimeMillis() - step3Start);
|
||||
|
||||
// 步骤4: 查询主要的申请信息
|
||||
long step4Start = System.currentTimeMillis();
|
||||
Optional<LeaseApplyInfo> optionalInfo = Optional.ofNullable(leaseApplyInfoMapper.selectLeaseApplyInfoById(leaseApplyInfo));
|
||||
stepTimes.put("查询主要申请信息", System.currentTimeMillis() - step4Start);
|
||||
LeaseApplyRequestVo leaseApplyRequestVo = new LeaseApplyRequestVo();
|
||||
// 查询领用出库数据
|
||||
|
||||
// 步骤5: 查询领用出库数据(可选)
|
||||
if (StringUtils.isNotBlank(publishTask)) {
|
||||
long step5Start = System.currentTimeMillis();
|
||||
leaseApplyInfo.setPublishTask(publishTask);
|
||||
List<LeaseApplyInfo> leaseApplyOutList = leaseApplyInfoMapper.selectPublishList(leaseApplyInfo);
|
||||
if (!CollectionUtils.isEmpty(leaseApplyOutList)) {
|
||||
|
|
@ -126,8 +150,13 @@ public class LeaseApplyInfoServiceImpl implements ILeaseApplyInfoService {
|
|||
}
|
||||
optionalInfo = Optional.of(leaseApplyOutList.get(0));
|
||||
}
|
||||
stepTimes.put("查询领用出库数据", System.currentTimeMillis() - step5Start);
|
||||
} else {
|
||||
stepTimes.put("查询领用出库数据", 0L); // 跳过此步骤
|
||||
}
|
||||
optionalInfo.ifPresent(info -> {
|
||||
// 步骤6: 查询附件信息
|
||||
long step6Start = System.currentTimeMillis();
|
||||
BmFileInfo bmFileInfo = new BmFileInfo();
|
||||
bmFileInfo.setModelId(id);
|
||||
bmFileInfo.setTaskType(2);
|
||||
|
|
@ -136,14 +165,18 @@ public class LeaseApplyInfoServiceImpl implements ILeaseApplyInfoService {
|
|||
if (!CollectionUtils.isEmpty(bmFileInfoList)) {
|
||||
info.setBmFileInfos(bmFileInfoList);
|
||||
}
|
||||
stepTimes.put("查询附件信息", System.currentTimeMillis() - step6Start);
|
||||
|
||||
// 步骤7: 设置审批人签名和发料单位
|
||||
long step7Start = System.currentTimeMillis();
|
||||
/** 设置审批人签名url 防止代码冲突 **/
|
||||
String directAuditUrl = leaseApplyInfoMapper.getDirectAuditUrl(info);
|
||||
String directAuditUrl = leaseApplyInfoMapper.getDirectAuditUrl(info);
|
||||
info.setDirectAuditSignUrl(directAuditUrl);
|
||||
/** 设置审批人签名url 防止代码冲突 **/
|
||||
|
||||
/** 设置发料单位 防止代码冲突 **/
|
||||
if(info.getDirectAuditBy() != null){
|
||||
String sendUnit = leaseApplyInfoMapper.getSendUnit(info);
|
||||
String sendUnit = leaseApplyInfoMapper.getSendUnit(info);
|
||||
info.setSendUnit(sendUnit);
|
||||
}
|
||||
// 电子签名进行base64拼接
|
||||
|
|
@ -151,15 +184,21 @@ public class LeaseApplyInfoServiceImpl implements ILeaseApplyInfoService {
|
|||
info.setLeaseSignUrl("data:image/png;base64," + info.getLeaseSignUrl());
|
||||
}
|
||||
/** 设置发料单位 防止代码冲突 **/
|
||||
|
||||
stepTimes.put("设置签名和单位信息", System.currentTimeMillis() - step7Start);
|
||||
|
||||
leaseApplyRequestVo.setLeaseApplyInfo(info);
|
||||
// 获取领料单详情
|
||||
List<LeaseApplyDetails> details = new ArrayList<>();
|
||||
// 步骤8: 获取领料单详情
|
||||
long step8Start = System.currentTimeMillis();
|
||||
List<LeaseApplyDetails> details;
|
||||
if (leaseApplyInfo.getUserId() != null) {
|
||||
details = leaseApplyDetailsMapper.selectLeaseApplyDetailsList(new LeaseApplyDetails(info.getId(), keyword, userId, null));
|
||||
} else {
|
||||
details = leaseApplyDetailsMapper.selectLeaseApplyDetailsList(new LeaseApplyDetails(info.getId(), keyword, null, typeIdList));
|
||||
}
|
||||
stepTimes.put("获取领料单详情", System.currentTimeMillis() - step8Start);
|
||||
|
||||
// 步骤81: 获取领料单详情
|
||||
long step81Start = System.currentTimeMillis();
|
||||
// 走单独的领用详情查询
|
||||
if (StringUtils.isNotBlank(publishTask)) {
|
||||
// 根据领用批次查询领用详情
|
||||
|
|
@ -174,15 +213,45 @@ public class LeaseApplyInfoServiceImpl implements ILeaseApplyInfoService {
|
|||
}
|
||||
}
|
||||
}
|
||||
stepTimes.put("领用发布查询", System.currentTimeMillis() - step81Start);
|
||||
|
||||
if (!CollectionUtils.isEmpty(details)) {
|
||||
leaseApplyRequestVo.setLeaseApplyDetailsList(details);
|
||||
for (LeaseApplyDetails detail : details) {
|
||||
// 获取编码详情
|
||||
List<MaCodeVo> maCodeVoList = leaseApplyDetailsMapper.getCodeList(id, detail.getTypeId());
|
||||
if (!CollectionUtils.isEmpty(maCodeVoList)) {
|
||||
detail.setMaCodeVoList(maCodeVoList);
|
||||
|
||||
// 步骤9: 获取编码详情(批量优化)
|
||||
long step9Start = System.currentTimeMillis();
|
||||
|
||||
// 收集所有typeId
|
||||
List<Long> typeIds = details.stream()
|
||||
.map(LeaseApplyDetails::getTypeId)
|
||||
.filter(Objects::nonNull)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (!CollectionUtils.isEmpty(typeIds)) {
|
||||
// 一次性批量查询所有编码详情
|
||||
List<MaCodeVo> allMaCodeList = leaseApplyDetailsMapper.getCodeListBatch(id, typeIds);
|
||||
|
||||
// 按typeId分组
|
||||
Map<String, List<MaCodeVo>> maCodeMap = allMaCodeList.stream()
|
||||
.filter(maCode -> maCode.getTypeId() != null)
|
||||
.collect(Collectors.groupingBy(MaCodeVo::getTypeId));
|
||||
|
||||
// 分配给对应的detail
|
||||
for (LeaseApplyDetails detail : details) {
|
||||
if (detail.getTypeId() != null) {
|
||||
List<MaCodeVo> maCodeVoList = maCodeMap.get(detail.getTypeId());
|
||||
if (!CollectionUtils.isEmpty(maCodeVoList)) {
|
||||
detail.setMaCodeVoList(maCodeVoList);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stepTimes.put("获取编码详情(批量优化)", System.currentTimeMillis() - step9Start);
|
||||
|
||||
// 步骤10: 处理审批签名
|
||||
long step10Start = System.currentTimeMillis();
|
||||
// 提取details中的signType和signUrl,单独作为一个集合,并去重
|
||||
List<LeaseOutSign> approveSignList = details.stream()
|
||||
.filter(detail -> detail.getSignUrl() != null)
|
||||
|
|
@ -197,9 +266,13 @@ public class LeaseApplyInfoServiceImpl implements ILeaseApplyInfoService {
|
|||
}
|
||||
leaseApplyRequestVo.setApproveSignList(approveSignList);
|
||||
}
|
||||
|
||||
stepTimes.put("处理审批签名", System.currentTimeMillis() - step10Start);
|
||||
} else {
|
||||
stepTimes.put("获取编码详情", 0L);
|
||||
stepTimes.put("处理审批签名", 0L);
|
||||
}
|
||||
// 根据id查询领料出库情况,查询出库库管员电子签名详情
|
||||
// 步骤11: 查询出库库管员电子签名详情
|
||||
long step11Start = System.currentTimeMillis();
|
||||
List<LeaseOutSign> outSignList = leaseApplyInfoMapper.selectLeaseApplyOutList(id);
|
||||
if (!CollectionUtils.isEmpty(outSignList)) {
|
||||
for (LeaseOutSign applyInfo : outSignList) {
|
||||
|
|
@ -209,8 +282,10 @@ public class LeaseApplyInfoServiceImpl implements ILeaseApplyInfoService {
|
|||
}
|
||||
leaseApplyRequestVo.setKgSignList(outSignList);
|
||||
}
|
||||
stepTimes.put("查询库管员签名", System.currentTimeMillis() - step11Start);
|
||||
|
||||
// 根据id查询领料出库情况,查询领料人电子签名详情
|
||||
// 步骤12: 查询领料人电子签名详情
|
||||
long step12Start = System.currentTimeMillis();
|
||||
List<LeaseOutSign> signList = leaseApplyInfoMapper.selectOutList(id, null);
|
||||
if (!CollectionUtils.isEmpty(signList)) {
|
||||
for (LeaseOutSign applyInfo : signList) {
|
||||
|
|
@ -220,12 +295,21 @@ public class LeaseApplyInfoServiceImpl implements ILeaseApplyInfoService {
|
|||
}
|
||||
leaseApplyRequestVo.setOutSignList(signList);
|
||||
}
|
||||
stepTimes.put("查询领料人签名", System.currentTimeMillis() - step12Start);
|
||||
|
||||
});
|
||||
|
||||
// 性能分析总结
|
||||
long totalTime = System.currentTimeMillis() - startTime;
|
||||
DateTimeHelper.logPerformanceAnalysis("selectLeaseApplyInfoById", totalTime, stepTimes);
|
||||
|
||||
return leaseApplyRequestVo;
|
||||
} catch (Exception e) {
|
||||
// 记录异常日志
|
||||
// 记录异常日志和性能数据
|
||||
long totalTime = System.currentTimeMillis() - startTime;
|
||||
log.error("selectLeaseApplyInfoById执行异常,耗时: {}ms, id: {}, 错误: {}", totalTime, id, e.getMessage());
|
||||
DateTimeHelper.logPerformanceAnalysis("selectLeaseApplyInfoById(异常)", totalTime, stepTimes);
|
||||
|
||||
System.err.println("Error occurred while selecting lease apply info by ID: " + id + e.getMessage());
|
||||
throw new RuntimeException("查询失败,请联系管理员");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -527,6 +527,45 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
and mt.type_id = #{typeId}
|
||||
</select>
|
||||
|
||||
<!-- 批量获取编码详情,优化N+1查询问题 -->
|
||||
<select id="getCodeListBatch" resultType="com.bonus.common.biz.domain.lease.MaterialLeaseMaCodeDto">
|
||||
SELECT
|
||||
mt.type_id as typeId,
|
||||
mt1.type_name as materialName,
|
||||
mt.type_name as materialModel,
|
||||
mm.ma_id as maId,
|
||||
mm.ma_code as maCode,
|
||||
'在用' as maStatus,
|
||||
mm.ma_status as status
|
||||
FROM
|
||||
clz_lease_out_details lod
|
||||
LEFT JOIN ma_machine mm ON lod.ma_id = mm.ma_id
|
||||
LEFT JOIN ma_type mt ON mm.type_id = mt.type_id
|
||||
AND mt.del_flag = '0'
|
||||
LEFT JOIN ma_type mt1 ON mt.parent_id = mt1.type_id
|
||||
AND mt1.del_flag = '0'
|
||||
WHERE
|
||||
lod.parent_id = #{id}
|
||||
<if test="typeIds != null and typeIds.size() > 0">
|
||||
AND mt.type_id IN
|
||||
<foreach collection="typeIds" item="typeId" open="(" separator="," close=")">
|
||||
#{typeId}
|
||||
</foreach>
|
||||
</if>
|
||||
ORDER BY mt.type_id, mm.ma_code
|
||||
</select>
|
||||
|
||||
<!-- 批量查询设备是否已被领料 -->
|
||||
<select id="getUsedMaIdsBatch" resultType="java.lang.Long">
|
||||
SELECT DISTINCT csa.ma_id
|
||||
FROM clz_slt_agreement_info csa
|
||||
WHERE csa.status = '0'
|
||||
AND csa.ma_id IN
|
||||
<foreach collection="maIds" item="maId" open="(" separator="," close=")">
|
||||
#{maId}
|
||||
</foreach>
|
||||
</select>
|
||||
|
||||
<select id="getOutNum" resultType="com.bonus.material.clz.domain.lease.MaterialLeaseApplyDetails">
|
||||
SELECT
|
||||
lad.parent_id as parentId,
|
||||
|
|
|
|||
|
|
@ -72,7 +72,46 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
</sql>
|
||||
|
||||
<select id="selectLeaseApplyDetailsList" parameterType="com.bonus.material.lease.domain.LeaseApplyDetails" resultMap="LeaseApplyDetailsResult">
|
||||
<include refid="selectLeaseApplyDetailsVo"/>
|
||||
select
|
||||
lad.id, lad.parent_id, mt.type_id, mt.type_name, mt2.type_name as ma_type_name,
|
||||
CASE mt.manage_type
|
||||
WHEN 0 THEN
|
||||
IFNULL(subquery0.num, 0)
|
||||
ELSE
|
||||
IFNULL(mt.storage_num, 0)
|
||||
END as storage_num,
|
||||
mt.manage_type as manageType,
|
||||
(lad.pre_num - IF(lad.al_num IS NULL,'0',lad.al_num)) AS outNum,
|
||||
IFNULL(lad.pre_num,0) as pre_num,
|
||||
IFNULL(lad.audit_num,0) as audit_num,
|
||||
IFNULL(lad.al_num,0) as al_num,
|
||||
IFNULL(lad.status,0) as status, mt.unit_name,mt.unit_value,
|
||||
lad.create_by, lad.create_time, lad.update_by, lad.update_time, lad.remark, lad.company_id,
|
||||
mt4.type_id as firstId,
|
||||
su.sign_url as signUrl,
|
||||
su.sign_type as signType
|
||||
from
|
||||
lease_apply_details lad
|
||||
left join
|
||||
ma_type mt on lad.type_id = mt.type_id and mt.`level` = '4' and mt.del_flag = '0'
|
||||
left join
|
||||
ma_type mt2 on mt2.type_id = mt.parent_id and mt2.`level` = '3' and mt2.del_flag = '0'
|
||||
left join ma_type mt3 ON mt2.parent_id = mt3.type_id and mt3.del_flag = '0'
|
||||
left join ma_type mt4 ON mt3.parent_id = mt4.type_id and mt4.del_flag = '0'
|
||||
left join sys_user su on su.user_id = mt3.keep_user_id
|
||||
left join (SELECT mt.type_id,
|
||||
mt2.type_name AS typeName,
|
||||
mt.type_name AS typeModelName,
|
||||
count(mm.ma_id) num
|
||||
FROM ma_machine mm
|
||||
LEFT JOIN ma_type mt ON mt.type_id = mm.type_id
|
||||
LEFT JOIN ma_type mt2 ON mt2.type_id = mt.parent_id
|
||||
WHERE mm.ma_code is not null and mm.ma_status in (1)
|
||||
GROUP BY mt.type_id) AS subquery0 ON subquery0.type_id = mt.type_id
|
||||
<if test="userId != null">
|
||||
JOIN ma_type_keeper mtk ON mtk.type_id = lad.type_id AND mtk.user_id = #{userId}
|
||||
</if>
|
||||
|
||||
<where>
|
||||
<if test="parentId != null "> and lad.parent_id = #{parentId}</if>
|
||||
<if test="keyword != null and keyword != ''">
|
||||
|
|
@ -325,6 +364,32 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
and mt.type_id = #{typeId}
|
||||
</select>
|
||||
|
||||
<!-- 批量获取编码详情,优化N+1查询问题 -->
|
||||
<select id="getCodeListBatch" resultType="com.bonus.material.back.domain.vo.MaCodeVo">
|
||||
SELECT
|
||||
mt.type_id as typeId,
|
||||
mt1.type_name as materialName,
|
||||
mt.type_name as typeName,
|
||||
mm.ma_id as maId,
|
||||
mm.ma_code as maCode
|
||||
FROM
|
||||
lease_out_details lod
|
||||
LEFT JOIN ma_machine mm ON lod.ma_id = mm.ma_id
|
||||
LEFT JOIN ma_type mt ON mm.type_id = mt.type_id
|
||||
AND mt.del_flag = '0'
|
||||
LEFT JOIN ma_type mt1 ON mt.parent_id = mt1.type_id
|
||||
AND mt1.del_flag = '0'
|
||||
WHERE
|
||||
lod.parent_id = #{id}
|
||||
<if test="typeIds != null and typeIds.size() > 0">
|
||||
AND mt.type_id IN
|
||||
<foreach collection="typeIds" item="typeId" open="(" separator="," close=")">
|
||||
#{typeId}
|
||||
</foreach>
|
||||
</if>
|
||||
ORDER BY mt.type_id, mm.ma_code
|
||||
</select>
|
||||
|
||||
<select id="selectLeaseOutDetailsList" resultType="com.bonus.material.lease.domain.vo.LeaseOutVo">
|
||||
SELECT
|
||||
mt1.type_name AS typeName,
|
||||
|
|
|
|||
Loading…
Reference in New Issue