package com.infynova.udi.service.helper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.infynova.udi.entity.MatchData; import com.infynova.udi.entity.MatchTemp; import com.infynova.udi.enums.match.MatchStatusEnum; import com.infynova.udi.enums.match.MatchUpdateStatusEnum; import com.infynova.udi.mapper.MatchDataMapper; import com.infynova.udi.mapper.MatchTempMapper; import com.infynova.udi.mapper.UdiMapper; import com.infynova.udi.vo.TaskVo; import com.infynova.udi.vo.UdiMatchRateVo; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.*; import java.util.stream.Collectors; /** * * @author liudong * 2024/3/5 13:44 * @version 1.0 */ @Slf4j @Component("matchHelper") public class MatchHelper { @Resource private MatchDataMapper matchDataMapper; @Resource private MatchTempMapper matchTempMapper; @Resource private UdiMapper udiMapper; @Resource private TaskHelper taskHelper; @Resource private UdiLock udiLock; @Resource private MatchESHelper matchESHelper; private static final BigDecimal ONE_HUNDRED_RATE = new BigDecimal("100"); private String getKey(String key){ return "match:" + key; } /** * 对码匹配 */ public void tranMatchRate(){ Set<Long> taskIdSet = matchDataMapper.queryMatchWaitingTask(); if(CollectionUtils.isEmpty(taskIdSet)){ log.info("对码匹配-没有需要对码匹配的数据"); } for (Long taskId : taskIdSet) { // 加锁机制 String key = this.getKey(String.valueOf(taskId)); Boolean hasKey = udiLock.hasKey(key); if(hasKey){ log.info("对码匹配-taskId:{},对码已在运行中;",key); continue; }else{ try { log.info("对码匹配-taskId:{},上锁;",key); udiLock.lock(key,1440L); // 通知任务修改任务状态 taskHelper.taskMatchStatusStart(taskId); // 对码核心逻辑 this.taskMatch(taskId); // 结束 taskHelper.taskMatchStatusFinish(taskId); } finally { log.info("对码匹配-taskId:{},解锁;",key); udiLock.del(key); } } } } /** * 对码核心逻辑 */ @Transactional(propagation = Propagation.REQUIRES_NEW) public void taskMatch(Long taskId){ // 每一次转换最大数量100 int pageNo = 1; int oneMaxNum = 100; TaskVo taskVo = taskHelper.getTaskVo(taskId); log.info("对码匹配-taskNo:{}",taskVo.getTask().getTaskNo()); while (true){ // 查询未开始的数据 LambdaQueryWrapper<MatchData> queryWrapper = new LambdaQueryWrapper<>(); Page<MatchData> page = new Page<>(pageNo, oneMaxNum); queryWrapper.eq(MatchData::getMatchStatus, MatchStatusEnum.WAITING.getCode()); queryWrapper.eq(MatchData::getTaskId, taskId); Page<MatchData> dataPage = matchDataMapper.selectPage(page, queryWrapper); if(CollectionUtils.isEmpty(dataPage.getRecords())){ break; } for (MatchData matchData : dataPage.getRecords()) { this.match(taskId,matchData); } pageNo++; } } /** * 查询匹配且生成匹配数据 */ private void match(Long taskId, MatchData matchData){ // 查询udi //Page<UdiVo> voPage = this.queryUdi(udiPageNo,pageSize,matchData); List<UdiMatchRateVo> matchRateVoAllList = new ArrayList<>(); boolean haveHundredFlag = false; if(StringUtils.isNotBlank(matchData.getUdiCode())){ // 去匹配udiCode List<UdiMatchRateVo> matchRateVoList = this.queryUdiNew(matchData); this.setSource(matchRateVoList,1); matchRateVoAllList.addAll(matchRateVoList); haveHundredFlag = this.haveHundred(matchRateVoList); } if(!haveHundredFlag && StringUtils.isNotBlank(matchData.getYiBaoCode())){ // 去匹配YiBaoCode List<UdiMatchRateVo> matchRateVoList = this.queryYiBaoCode(matchData); this.setSource(matchRateVoList,2); matchRateVoAllList.addAll(matchRateVoList); haveHundredFlag = this.haveHundred(matchRateVoList); } if(!haveHundredFlag && (StringUtils.isNotBlank(matchData.getCompanyName()) || StringUtils.isNotBlank(matchData.getProductFactoryCode())) ){ // 厂家+产品货号/编号 List<UdiMatchRateVo> matchRateVoList = matchESHelper.three(matchData.getCompanyName(), matchData.getProductFactoryCode()); this.setSource(matchRateVoList,3); matchRateVoAllList.addAll(matchRateVoList); haveHundredFlag = this.haveHundred(matchRateVoList); } if (!haveHundredFlag && (StringUtils.isNotBlank(matchData.getBrandName()) || StringUtils.isNotBlank(matchData.getProductName()) || StringUtils.isNotBlank(matchData.getSpecification()) || StringUtils.isNotBlank(matchData.getModel())) ) { // 品牌+名称+规格+型号; List<UdiMatchRateVo> matchRateVoList = matchESHelper.four(matchData.getBrandName(), matchData.getProductName(), matchData.getSpecification(), matchData.getModel()); this.setSource(matchRateVoList,4); matchRateVoAllList.addAll(matchRateVoList); } // 需要按照匹配率高低排序,留20条 List<UdiMatchRateVo> matchRateVoSortList = this.getTopNMatchRates(matchRateVoAllList, 20); // 计算匹配率 List<MatchTemp> matchTempList = new ArrayList<>(); boolean hundredRate = false; for (UdiMatchRateVo udiVo : matchRateVoSortList) { // 核心匹配率逻辑 BigDecimal matchRate = udiVo.getMatchRatio(); if(matchRate.compareTo(ONE_HUNDRED_RATE) == 0){ hundredRate = true; } MatchTemp matchTemp = new MatchTemp(); BeanUtils.copyProperties(udiVo,matchTemp); Long matchId = matchData.getId(); matchTemp.setProductId(udiVo.getId()); matchTemp.setOriginalRecord(false); matchTemp.setMatchId(matchId); matchTemp.setTaskId(taskId); matchTemp.setMatchRate(matchRate); matchTemp.setCreateBy(null); matchTemp.setCreateName(null); matchTemp.setCreateTime(LocalDateTime.now()); matchTemp.setUpdateBy(null); matchTemp.setUpdateName(null); matchTemp.setUpdateTime(LocalDateTime.now()); matchTempList.add(matchTemp); } // 存在查询到了数据,但是匹配率全为0的情况 if(CollectionUtils.isEmpty(matchTempList)){ // 如果为空 matchData.setMatchStatus(MatchStatusEnum.NO.getCode()); matchData.setUpdateStatus(MatchUpdateStatusEnum.NOT_APPLICABLE.getCode()); matchData.setUpdateTime(LocalDateTime.now()); matchDataMapper.updateById(matchData); }else{ if(hundredRate){ // 100%对码 matchData.setMatchStatus(MatchStatusEnum.HUNDRED.getCode()); }else{ // 部分对码 matchData.setMatchStatus(MatchStatusEnum.SECTION.getCode()); } matchData.setUpdateStatus(MatchUpdateStatusEnum.PENDING.getCode()); matchData.setUpdateTime(LocalDateTime.now()); matchDataMapper.updateById(matchData); // 进行排序 List<MatchTemp> sortList = matchTempList.stream().sorted(Comparator.comparing(MatchTemp::getMatchRate)).collect(Collectors.toList()); matchTempMapper.insertBatchSomeColumn(sortList); } } /** * 判断是否有百分之百的数据存在 * @param list * @return */ private boolean haveHundred(List<UdiMatchRateVo> list){ for (UdiMatchRateVo udiMatchRateVo : list) { BigDecimal matchRatio = udiMatchRateVo.getMatchRatio(); if(matchRatio.compareTo(ONE_HUNDRED_RATE) == 0){ return true; } } return false; } private List<UdiMatchRateVo> getTopNMatchRates(List<UdiMatchRateVo> matchRateVoAllList, int n){ // 按照 matchRatio 字段降序排序,并且保留前 n 条数据 // todo 是否需要根据id进行去重? return matchRateVoAllList.stream() .sorted((vo1, vo2) -> vo2.getMatchRatio().compareTo(vo1.getMatchRatio())) .limit(n) .collect(Collectors.toList()); } /** * 查询udi */ private List<UdiMatchRateVo> queryUdiNew(MatchData matchData){ String udiCode = matchData.getUdiCode(); // String udiCodeRegex = this.splitWord(udiCode); List<String> udiCodeRegexList = new ArrayList<>(); String lastUdi = udiCode; // 规定udi截取的最小长度 int udiCodeMinLength = 8; while (true){ String subUdi = decrementUDICode(lastUdi,2,udiCodeMinLength); if(Objects.equals(subUdi,lastUdi)){ break; }else{ lastUdi = subUdi; udiCodeRegexList.add(subUdi); } } return udiMapper.getUdiCodeRegex(udiCode,udiCodeRegexList); } // 递减 Code 的函数 private String decrementUDICode(String code, int decrementAmount, int minLength) { int length = code.length(); if(length<=minLength){ return code; } return code.substring(0, length - decrementAmount); } /** * 查询C码 */ private List<UdiMatchRateVo> queryYiBaoCode(MatchData matchData){ String yiBaoCode = matchData.getYiBaoCode(); List<String> yiBaoCodeRegexList = new ArrayList<>(); String lastYiBaoCode = yiBaoCode; // 规定udi截取的最小长度 int yiBaoCodeMinLength = 8; while (true){ String subYiBaoCode = decrementUDICode(lastYiBaoCode,2,yiBaoCodeMinLength); if(Objects.equals(subYiBaoCode,lastYiBaoCode)){ break; }else{ lastYiBaoCode = subYiBaoCode; yiBaoCodeRegexList.add(subYiBaoCode); } } return udiMapper.getYiBaoCodeRegex(yiBaoCode,yiBaoCodeRegexList); } private void setSource(List<UdiMatchRateVo> list, Integer source){ if(CollectionUtils.isNotEmpty(list)){ list.forEach(i -> i.setSource(source)); } } }