商品中心—6.商品考核系统的技术文档二

大纲

1.基于大数据系统的商品考核数据指标

2.基于商品考核数据指标的商品考核流程

3.商品考核失败后的处理 + 考核流程的设计

4.商品考核系统数据库模型设计

5.商品考核系统核心接口

6.商品生命周期系统的定时考核任务

6.商品生命周期系统的定时考核任务

(1)定时任务处理考核指标数据的设计

(2)定时任务处理考核指标数据的实现

(3)定时任务基于考核结果设置商品可采可补可售

(4)考核结果数据的Excel导出

(1)定时任务处理考核指标数据的设计

定时任务会根据考核指标数据,计算出商品的考核结果。定时查询出考核指标数据,根据商品质量、诚信、销量、利润等数据,分别对试销期和滞销期商品进⾏考核,并将考核结果写⼊考核结果缓冲表。

在⼀轮考核任务中,对于配置了⽩名单的品类商品,可以免除本轮次考核,免除本轮次考核的考核状态为免考核。

质量和诚信问题,具有⼀票否决权,⼆者任⼀不满⾜,则本轮考核不通过。滞销期考核也直接不通过,考核状态为考核不通过。

销售⽐率、利润率、退货⽐率、客诉率、损耗率五个考核指标,通过率未超过考核指标配置的考核通过率,则本轮考核不通过。此时状态是考核中,需⽐较连续失败次数是否超过阈值,来决定考核不通过。

对于试销期商品,本轮考核不通过则该商品直接考核不通过。对于滞销期商品,本轮考核不通过需⽐较考核指标配置的连续失败次数,只有达到了配置的连续失败次数,才会考核不通过。如果没有达到连续失败次数,则对该数值进⾏累加,然后等待下⼀轮考核。

当本轮次考核结束,会将计算得到的考核结果写⼊考核结果缓冲表。同时让该考核指标数据失效,避免下⼀轮重复拉取该考核指标数据。

(2)定时任务处理考核指标数据的实现

一.任务执⾏说明

考核任务的周期在xxljob的管理界⾯中指定,并且调度任务时需传⼊参数。参数要指定该任务的考核类型,是试销期考核还是滞销期考核,不传⼊参数指定考核类型则会抛出异常。

二.任务流程图

三.任务具体实现

步骤一:查询大数据系统计算出来的商品考核指标数据
步骤二:考核指标数据对应的商品的生命周期与考核类型对比
步骤三:过滤出在白名单中免考核的考核指标数据
步骤四:过滤出有质量或者诚信问题的商品指标数据
步骤五:分页查询出考核配置数据
步骤六:对商品指标数据进行考核以及划分结果
步骤七:商品考核数据分多种类别写入结果缓冲表
步骤八:将参与本轮考核计算的考核指标数据都设置为失效状态
//生命周期阶段考核
@Component
public class CalculateExpriResultSchedule {//考核结果资源管理@Autowiredprivate ItemExpriResultRepository itemExpriResultRepository;//生命周期资源管理@Autowiredprivate ItemPeriodStageRepository itemPeriodStageRepository;//考核白名单资源管理@Autowiredprivate CategoryExpriWhiteRepository categoryExpriWhiteRepository;//商品考核资源管理@Autowiredprivate ProductExpriRepository productExpriRepository;//考核结果对象克隆@Resourceprivate ItemExpriResultConverter itemExpriResultConverter;//MQ producer@Autowiredprivate DefaultProducer defaultProducer;//定时任务:根据商品的质量和诚信问题、销量、客诉率等数据,对试销期、成熟期商品进行考核,并将考核结果写入考核结果缓冲表//一般情况下:每天凌晨3点钟执行@XxlJob("assessing")@Transactional(rollbackFor = Exception.class)public void assessing() {//1.获取考核类型参数(试销期1,成熟期2)Integer assessmentType = getAssessmentType();//2.查询大数据系统计算出来的商品考核指标数据List<ProductExpriDataResultDTO> totalExpriDataList = productExpriRepository.listExpriDataResultByPage(assessmentType);//3.考核指标数据对应的itemIdListList<String> itemIdList = getItemIdList(totalExpriDataList);//4.考核指标数据对应的categoryIdListList<Integer> categoryIdList = getCategoryIdList(totalExpriDataList);//5.查询生命周期阶段Map<String, ItemPeriodStageDTO> itemPeriodStageMap = listItemPeriodStageByPage(assessmentType, itemIdList);//6.过滤掉商品所处生命周期阶段和当前考核类型不匹配的数据,并返回生命周期阶段MaptotalExpriDataList = filterPeriodConsistencyToMap(totalExpriDataList, itemPeriodStageMap);//7.过滤出在白名单中免考核的考核指标数据List<ProductExpriDataResultDTO> nonAssessmentExpriDataList = filterNonAssessmentToList(totalExpriDataList, categoryIdList);//8.分页查询出考核配置数据Map<Integer, ProductExpriConfigurationDTO> expriConfigurationMap = listExpriConfigurationByPage(categoryIdList, assessmentType);//9.批量查质量或诚信问题数据,并过滤出存在质量或诚信问题,考核不通过的数据List<ProductExpriDataResultDTO> assessFailureList = filterConformingIntegrityRecordToList(totalExpriDataList, itemIdList);//10.比较销售额和销售率、客诉率等考核指标项,将考核数据分别划分到:考核不通过、考核通过、考核中三个集合中List<ProductExpriDataResultDTO> assessingList = filterSalesVolumePassTargetToList(totalExpriDataList, assessFailureList, expriConfigurationMap, assessmentType);//11.将考核不通过、考核中、考核通过,免考核的考核指标数据,批量写入到考核结果缓冲表batchInsertAssessmentResult(itemIdList, assessFailureList, assessingList, totalExpriDataList, nonAssessmentExpriDataList, itemPeriodStageMap);//12.将参与本轮考核计算的考核指标数据都设置为失效状态batchInvalidExpriData(itemIdList);}...
}

步骤一:查询大数据系统计算的商品考核指标数据

//商品考核资源管理
@Repository
public class ProductExpriRepository {...//分页查询某考核类型下所有生效的商品考核指标数据public List<ProductExpriDataResultDTO> listExpriDataResultByPage(Integer assessmentType) {//构造查询条件LambdaQueryWrapper<ProductExpriDataResultDO> queryWrapper = Wrappers.lambdaQuery();//是否生效queryWrapper.eq(ProductExpriDataResultDO::getIsEffective, YesOrNoEnum.YES.getCode());//是否删除queryWrapper.eq(ProductExpriDataResultDO::getDelFlag, YesOrNoEnum.YES.getCode());//考核类型:试销期考核/成熟期考核queryWrapper.eq(ProductExpriDataResultDO::getAssessmentType, assessmentType);//分页查询存放数据的总集合List<ProductExpriDataResultDO> results = new ArrayList<>();int pageNum = 1;//设置每次查询的数据量,最大为200int pageSize = ProductConstants.QUERY_ITEM_MAX_COUNT;Page<ProductExpriDataResultDO> page = new Page<>(pageNum, pageSize);//查询第一页数据Page<ProductExpriDataResultDO> pageResult = productExpriDataResultMapper.selectPage(page, queryWrapper);//判断是否还有数据,还有数据则页码 + 1继续执行分页查询List<ProductExpriDataResultDO> batchResult = pageResult.getRecords();try {while (pageNum <= pageResult.getTotal()) {results.addAll(batchResult);pageNum += 1;page.setCurrent(pageNum);pageResult = productExpriDataResultMapper.selectPage(page, queryWrapper);batchResult = pageResult.getRecords();//每次循环获取数据后,休眠20ms,避免对数据库造成太大压力Thread.sleep(20);}} catch (InterruptedException e) {throw new BaseBizException(ProductExceptionCode.PRODUCT_SQL);}//最后一组数据也放入结果集中results.addAll(page.getRecords());//转为DTOList<ProductExpriDataResultDTO> productExpriDataResultList = productExpriDataResultConverter.listEntityToDTO(results);return Objects.isNull(productExpriDataResultList) ? Lists.newArrayList() : productExpriDataResultList;}...
}

步骤二:考核指标数据对应的商品的生命周期与考核类型对比

@Component
public class CalculateExpriResultSchedule {...//过滤掉所处的生命周期阶段和当前考核类型不匹配的数据//@param totalExpriDataList 考核指标数据集合//@param itemPeriodStageMap 生命周期Map,key为itemId//@return 生命周期阶段和考核类型匹配的考核指标数据集合private List<ProductExpriDataResultDTO> filterPeriodConsistencyToMap(List<ProductExpriDataResultDTO> totalExpriDataList, Map<String, ItemPeriodStageDTO> itemPeriodStageMap) {//过滤考核指标数据totalExpriDataList = totalExpriDataList.stream().filter(productExpriDataResultDTO -> Objects.nonNull(itemPeriodStageMap.get(productExpriDataResultDTO.getItemId()))).collect(Collectors.toList());return totalExpriDataList;}...
}

步骤三:过滤出在白名单中免考核的考核指标数据

@Component
public class CalculateExpriResultSchedule {...//过滤出在白名单中的考核指标数据//@param totalExpriDataList 考核指标数据集合//@return 在白名单中的考核指标数据集合private List<ProductExpriDataResultDTO> filterNonAssessmentToList(List<ProductExpriDataResultDTO> totalExpriDataList, List<Integer> categoryIdList) {//1.分页查询出考核指标数据中对应的白名单所属的categoryIdListList<Integer> whiteCategoryIdList = categoryExpriWhiteRepository.queryExpriWhiteByCategoryIdList(categoryIdList);//2.过滤出在白名单范围内的考核指标数据List<ProductExpriDataResultDTO> nonAssessmentExpriDataList = totalExpriDataList.stream().filter(productExpriDataResultDTO -> whiteCategoryIdList.contains(productExpriDataResultDTO.getCategoryId())).collect(Collectors.toList());//3.剔除掉在白名单范围内的考核指标数据totalExpriDataList.removeAll(nonAssessmentExpriDataList);//4.返回在白名单内的考核指标数据return nonAssessmentExpriDataList;}...
}//考核白名单资源管理
@Repository
public class CategoryExpriWhiteRepository {...//分页查询出categoryIdList对应的白名单集合//@param categoryIdList 品类ID集合//@return 处于白名单范围内的categoryIdListpublic List<Integer> queryExpriWhiteByCategoryIdList(List<Integer> categoryIdList) {//处于白名单范围内的categoryIdListList<Integer> whiteCategoryIdList = new ArrayList<>();//一次最大查询200个数据,多个分页查询,这里做数据切割List<List<Integer>> splitList = DataCuttingUtil.dataCuttingString(categoryIdList, ProductConstants.QUERY_ITEM_MAX_COUNT);for (List<Integer> categoryIds : splitList) {LambdaQueryWrapper<CategoryExpriWhiteDO> queryWrapper = Wrappers.lambdaQuery();queryWrapper.in(CategoryExpriWhiteDO::getCategoryId, categoryIds);queryWrapper.eq(CategoryExpriWhiteDO::getDelFlag, YesOrNoEnum.YES.getCode());queryWrapper.eq(CategoryExpriWhiteDO::getActiveFlag, YesOrNoEnum.YES.getCode());//分批次查询出categoryIdList对应的白名单集合List<CategoryExpriWhiteDO> categoryResultList = categoryExpriWhiteMapper.selectList(queryWrapper);if (!CollectionUtils.isEmpty(categoryResultList)) {whiteCategoryIdList.addAll(categoryResultList.stream().map(CategoryExpriWhiteDO::getCategoryId).collect(Collectors.toList()));}}return whiteCategoryIdList;}...
}

步骤四:过滤出有质量或者诚信问题的商品指标数据

@Component
public class CalculateExpriResultSchedule {...//批量查询出质量或诚信问题数据集合,并对比考核数据,将对应的考核数据添加到考核不通过的集合中//@param totalExpriDataList 考核结果数据//@param itemIdList         商品ID集合//@return 考核不通过考核指标数据集合private List<ProductExpriDataResultDTO> filterConformingIntegrityRecordToList(List<ProductExpriDataResultDTO> totalExpriDataList, List<String> itemIdList) {//批量查询出质量诚信问题数据集合List<ProductBadIssuesResultDTO> productBadIssuesResultList = productExpriRepository.queryProductBadIssuesResultByItemIdList(itemIdList);//List转为Map,itemId作为keyMap<String, ProductBadIssuesResultDTO> productBadIssuesResultMap = productBadIssuesResultList.stream().collect(Collectors.toMap(ProductBadIssuesResultDTO::getItemId, productBadIssuesResult -> productBadIssuesResult, (oldKey, newKey) -> oldKey));//过滤出存在质量问题的考核指标数据List<ProductExpriDataResultDTO> assessFailureList = totalExpriDataList.stream().filter(productExpriDataResultDTO -> {//根据考核结果数据的itemId获取质量、诚信问题记录ProductBadIssuesResultDTO productBadIssuesResult = productBadIssuesResultMap.get(productExpriDataResultDTO.getItemId());//如果该考核结果数据存在质量诚信问题,就保留,否则就过滤掉if (productBadIssuesResult == null) {return false;}if (productBadIssuesResult.getContractIntegrity().equals(YesOrNoEnum.YES.getCode()) &&productBadIssuesResult.getNonConformingProduct().equals(YesOrNoEnum.YES.getCode())) {return false;}return true;}).collect(Collectors.toList());//剔除掉在存在质量问题的考核指标数据totalExpriDataList.removeAll(assessFailureList);//返回考核不通过的考核指标数据return assessFailureList;}...
}

步骤五:分页查询出考核配置数据

@Component
public class CalculateExpriResultSchedule {...//分页查考核配置数据//@param categoryIdList 品类ID集合//@return 封装为Map,categoryId作为keyprivate Map<Integer, ProductExpriConfigurationDTO> listExpriConfigurationByPage(List<Integer> categoryIdList, Integer assessmentType) {//分页查询出考核配置数据List<ProductExpriConfigurationDTO> productExpriConfigurationList = productExpriRepository.queryExpriConfigurationByCategoryIdList(categoryIdList, assessmentType);//List转为Map,categoryId作为keyMap<Integer, ProductExpriConfigurationDTO> productExpriConfigurationMap = productExpriConfigurationList.stream().collect(Collectors.toMap(ProductExpriConfigurationDTO::getCategoryId, productExpriConfiguration -> productExpriConfiguration, (oldKey, newKey) -> oldKey));return productExpriConfigurationMap;}...
}//商品考核资源管理
@Repository
public class ProductExpriRepository {...//分页查询出categoryIdList对应的考核配置集合//@param categoryIdList 品类ID集合//@return 考核配置集合public List<ProductExpriConfigurationDTO> queryExpriConfigurationByCategoryIdList(List<Integer> categoryIdList, Integer assessmentType) {//考核配置集合List<ProductExpriConfigurationDTO> productExpriConfigurationList = new ArrayList<>();//一次最大查询200个数据,多个分页查询,这里做数据切割List<List<Integer>> splitList = DataCuttingUtil.dataCuttingString(categoryIdList, ProductConstants.QUERY_ITEM_MAX_COUNT);for (List<Integer> categoryIds : splitList) {LambdaQueryWrapper<ProductExpriConfigurationDO> queryWrapper = Wrappers.lambdaQuery();queryWrapper.in(ProductExpriConfigurationDO::getCategoryId, categoryIds);queryWrapper.eq(ProductExpriConfigurationDO::getDelFlag, YesOrNoEnum.YES.getCode());queryWrapper.eq(ProductExpriConfigurationDO::getAssessmentType, assessmentType);//分批次查询出categoryIdList对应的考核配置集合List<ProductExpriConfigurationDO> configurationResultList = productExpriConfigurationMapper.selectList(queryWrapper);if (!CollectionUtils.isEmpty(configurationResultList)) {productExpriConfigurationList.addAll(productExpriConfigurationConverter.listEntityToDTO(configurationResultList));}}return productExpriConfigurationList;}...
}

步骤六:对商品指标数据进行考核以及划分结果

@Component
public class CalculateExpriResultSchedule {...//比较考核结果数据和考核配置数据,判断考核结果数据是否通过考核//销售额数据未达到要求,本轮考核不通过//销售比率、客诉率、损耗率等5项指标,达到要求的指标数量未超过考核配置中通过考核的指标数量,本轮考核不通过//对于试销期考核,本轮考核不通过,则考核状态即为不通过//但是对于滞销期考核,本轮考核不通过,还需要比较考核配置中的连续失败次数//如果连续失败次数超过了配置中的数值,则考核不通过//否则,对连续失败次数进行累加操作,考核状态为考核中//@param totalExpriDataList    考核指标数据(所有的考核数据集合)//@param assessFailureList     考核未通过的考核指标数据//@param expriConfigurationMap 考核指标配置//@param assessmentType        考核类型//@return 考核中的考核指标数据private List<ProductExpriDataResultDTO> filterSalesVolumePassTargetToList(List<ProductExpriDataResultDTO> totalExpriDataList,List<ProductExpriDataResultDTO> assessFailureList, Map<Integer, ProductExpriConfigurationDTO> expriConfigurationMap, Integer assessmentType) {//考核中的考核指标数据集合List<ProductExpriDataResultDTO> assessingList = new ArrayList<>();//批量查询出连续失败次数,作为Map,id是itemIdMap<String, Integer> failTimesMap = itemExpriResultRepository.listFailTimesByPage();for (ProductExpriDataResultDTO expriResult : totalExpriDataList) {//考核指标数据对应的考核配置ProductExpriConfigurationDTO expriConfiguration = expriConfigurationMap.get(expriResult.getCategoryId());//是否低于最低销售额if (expriResult.getSalesVolume() < expriConfiguration.getSalesVolume()) {fillListByAssessmentType(assessFailureList, assessingList, failTimesMap, assessmentType, expriResult, expriConfiguration);}//销售比率、客诉率、退货率是否符合要求if (!checkAssessment(expriResult, expriConfiguration)) {fillListByAssessmentType(assessFailureList, assessingList, failTimesMap, assessmentType, expriResult, expriConfiguration);}}//剔除掉考核不通过的考核指标数据totalExpriDataList.removeAll(assessFailureList);//剔除掉考核中的考核指标数据totalExpriDataList.removeAll(assessingList);//考核的流程执行到这里,totalExpriDataList中剩下的就是考核成功的考核指标数据//返回考核中的考核指标数据return assessingList;}//检查客诉率、退货率的指标是否符合要求//@param expriData          考核指标数据//@param expriConfiguration 考核指标配置//@return true:符合;false:不符合private boolean checkAssessment(ProductExpriDataResultDTO expriData, ProductExpriConfigurationDTO expriConfiguration) {//考核通过指标数量Integer passTargetConfig = expriConfiguration.getPassTarget();Integer passTarget = 0;//销售比例配置Integer soldPropotionConfig = expriConfiguration.getSoldPropotion();Integer soldPropotion = expriData.getSoldPropotion();if (soldPropotion >= soldPropotionConfig) {passTarget++;}//每单利润率配置Integer orderProfitMarginConfig = expriConfiguration.getOrderProfitMargin();Integer orderProfitMargin = expriData.getOrderProfitMargin();if (orderProfitMargin >= orderProfitMarginConfig) {passTarget++;}//退货比率配置Integer returnRateConfig = expriConfiguration.getReturnRate();Integer returnRate = expriData.getReturnRate();if (returnRate <= returnRateConfig) {passTarget++;}//客诉率配置Integer complaintRateConfig = expriConfiguration.getComplaintRate();Integer complaintRate = expriData.getComplaintRate();if (complaintRate <= complaintRateConfig) {passTarget++;}//损耗比率配置Integer lossRateConfig = expriConfiguration.getLossRate();Integer lossRate = expriData.getLossRate();if (lossRate <= lossRateConfig) {passTarget++;}if (passTarget >= passTargetConfig) {return true;}return false;}//考核不通过时,根据考核类型(试销期/滞销期)将考核指标数据,拆分填充到考核失败集合、考核中集合//@param assessFailureList  考核不通过集合//@param assessingList      考核中集合//@param failTimesMap       连续失败次数数据Map,key是itemId//@param assessmentType     考核类型//@param expriResult        考核指标数据//@param expriConfiguration 考核指标配置private void fillListByAssessmentType(List<ProductExpriDataResultDTO> assessFailureList,List<ProductExpriDataResultDTO> assessingList, Map<String, Integer> failTimesMap,Integer assessmentType, ProductExpriDataResultDTO expriResult, ProductExpriConfigurationDTO expriConfiguration) {//如果是试销考核if (assessmentType.equals(AssessmentTypeEnum.TRY_SALE_ASSESS.getCode())) {//直接添加到考核不通过集合中assessFailureList.add(expriResult);}//如果是滞销考核if (assessmentType.equals(AssessmentTypeEnum.GROWN_SALE_ASSESS.getCode())) {//比较失败次数Integer failTimes = failTimesMap.getOrDefault(expriResult.getItemId(), BigDecimal.ZERO.intValue());Integer failTimesConfig = expriConfiguration.getFailTimes();//如果小于配置的连续失败次数if (++failTimes < failTimesConfig) {//添加到考核中集合assessingList.add(expriResult);} else {//否则添加到考核不通过集合assessFailureList.add(expriResult);}}}...
}//考核结果资源管理
@Repository
public class ItemExpriResultRepository {...//分页查询出状态为考核中的考核缓冲结果数据中的连续失败次数集合//@return 连续失败次数集合public Map<String, Integer> listFailTimesByPage() {//构造查询条件LambdaQueryWrapper<ItemExpriResultBufferDO> queryWrapper = Wrappers.lambdaQuery();//考核状态为考核中queryWrapper.eq(ItemExpriResultBufferDO::getMarketExpriResult, MarketExpriResultEnum.UNDER_EXAMINATION.getCode());queryWrapper.eq(ItemExpriResultBufferDO::getDelFlag, YesOrNoEnum.YES.getCode());//分页查询存放数据的总集合Map<String, Integer> results = new HashMap<>();int pageNum = 1;//设置每次查询的数据量,最大为200int pageSize = ProductConstants.QUERY_ITEM_MAX_COUNT;Page<ItemExpriResultBufferDO> page = new Page<>(pageNum, pageSize);//查询第一页数据Page<ItemExpriResultBufferDO> pageResult = itemExpriResultBufferMapper.selectPage(page, queryWrapper);//判断是否还有数据,还有数据则页码+1继续执行分页查询List<ItemExpriResultBufferDO> batchResult = pageResult.getRecords();try {while (batchResult.size() >= ProductConstants.QUERY_ITEM_MAX_COUNT) {//将分页查询出来的list数据中的failTimes属性添加到Map集合中,itemId作为keyputAll(results, batchResult);pageNum += 1;page.setCurrent(pageNum);page = itemExpriResultBufferMapper.selectPage(page, queryWrapper);batchResult = page.getRecords();//每次循环获取数据后,休眠20ms,避免对数据库造成太大压力Thread.sleep(20);}} catch (InterruptedException e) {throw new BaseBizException(ProductExceptionCode.PRODUCT_SQL);}//最后一组数据也放入结果集中putAll(results, batchResult);return results;}...
}

步骤七:商品考核数据分多种类别写入结果缓冲表

@Component
public class CalculateExpriResultSchedule {...//批量将考核指标数据写入到考核结果缓冲表中//@param itemIdList                 itemId集合//@param assessFailureList          考核不通过的考核指标数据//@param assessingList              考核中的考核指标数据//@param totalExpriDataList         考核通过的考核指标数据//@param nonAssessmentExpriDataList 免考核的考核指标数据//@param itemPeriodStageMap         生命周期数据集合,key为itemIdprivate void batchInsertAssessmentResult(List<String> itemIdList, List<ProductExpriDataResultDTO> assessFailureList, List<ProductExpriDataResultDTO> assessingList, List<ProductExpriDataResultDTO> totalExpriDataList,List<ProductExpriDataResultDTO> nonAssessmentExpriDataList, Map<String, ItemPeriodStageDTO> itemPeriodStageMap) {//1.分页查询考核缓冲表中考核状态是考核中的数据Map<String, ItemExpriResultBufferDTO> itemExpriResultBufferMap = itemExpriResultRepository.listAssessingBufferResultByPage(itemIdList);//2.批量写入考核不通过的考核指标数据batchInsertByMarketExpriType(itemExpriResultBufferMap, assessFailureList, itemPeriodStageMap,MarketExpriResultEnum.FAIL_PASS_EXAMINATION.getCode(), BigDecimal.ONE.intValue(), BigDecimal.ONE.intValue());//3.批量写入考核中的考核指标数据batchInsertByMarketExpriType(itemExpriResultBufferMap, assessingList, itemPeriodStageMap,MarketExpriResultEnum.UNDER_EXAMINATION.getCode(), BigDecimal.ONE.intValue(), BigDecimal.ONE.intValue());//4.批量写入考核通过的考核指标数据batchInsertByMarketExpriType(itemExpriResultBufferMap, totalExpriDataList, itemPeriodStageMap,MarketExpriResultEnum.PASS_EXAMINATION.getCode(), BigDecimal.ZERO.intValue(), BigDecimal.ZERO.intValue());//5.批量写入免考核指标数据batchInsertByMarketExpriType(itemExpriResultBufferMap, nonAssessmentExpriDataList, itemPeriodStageMap,MarketExpriResultEnum.NON_EXAMINATION.getCode(), BigDecimal.ZERO.intValue(), BigDecimal.ZERO.intValue());}//根据考核状态批量将考核指标数据批量写入到考核结果缓冲表//考核不通过: 新增操作默认连续失败次数为1,修改操作连续失败次数增加1//考核中:    新增操作默认连续失败次数为1,修改操作连续失败次数增加1//考核通过:  新增操作默认连续失败次数为0,修改操作连续失败次数增加0//免考核:    新增操作默认连续失败次数为0,修改操作连续失败次数增加0//@param itemExpriResultBufferMap 已存在的考核状态为考核中的考核结果缓冲数据,key为itemId//@param expriDataResultList      考核指标数据集合//@param itemPeriodStageMap       生命周期数据,key为itemId//@param marketExpriResultType    考核状态:0:考核中;1:考核通过;2:考核不通过;3:不考核或暂停考核//@param defaultFailTimes         新增考核缓冲结果数据时,默认的失败次数//@param incrementFailTimes       修改考核缓冲结果数据时,增加的失败次数private void batchInsertByMarketExpriType(Map<String, ItemExpriResultBufferDTO> itemExpriResultBufferMap,List<ProductExpriDataResultDTO> expriDataResultList, Map<String, ItemPeriodStageDTO> itemPeriodStageMap,Integer marketExpriResultType, Integer defaultFailTimes, Integer incrementFailTimes) {//需要插入考核结果缓冲表的集合数据List<ItemExpriResultBufferRequest> insertRequest = new ArrayList<>();for (ProductExpriDataResultDTO result : expriDataResultList) {String itemId = result.getItemId();//如果考核结果缓冲表中不存在该记录if (Objects.isNull(itemExpriResultBufferMap.get(itemId))) {//准备批量插入数据ItemExpriResultBufferRequest request = new ItemExpriResultBufferRequest();request.setItemId(itemId);request.setCategoryId(result.getCategoryId());request.setPeriodStageId(itemPeriodStageMap.get(itemId).getProductPeriodId());request.setFailTimes(defaultFailTimes);request.setMarketExpriResult(marketExpriResultType);request.setDelFlag(YesOrNoEnum.YES.getCode());insertRequest.add(request);} else {//考核结果表中存在该记录,执行修改操作ItemExpriResultBufferRequest request = new ItemExpriResultBufferRequest();request.setItemId(itemId);Integer failTimes = itemExpriResultBufferMap.get(itemId).getFailTimes() + incrementFailTimes;request.setFailTimes(failTimes);request.setMarketExpriResult(marketExpriResultType);itemExpriResultRepository.updateItemExpriResultBuffer(request);}}//批量插入考核结果缓冲表itemExpriResultRepository.insertBatch(insertRequest);}...
}//考核结果资源管理
@Repository
public class ItemExpriResultRepository {...//分页查询考核缓冲表中考核状态是考核中的数据//@param itemIdList itemId集合//@return 考核数据Map,key为itemIdpublic Map<String, ItemExpriResultBufferDTO> listAssessingBufferResultByPage(List<String> itemIdList) {if (CollectionUtils.isEmpty(itemIdList)) {return CollectionUtils.newHashMap();}//考核中的考核结果缓冲数据集合Map<String, ItemExpriResultBufferDTO> resultMap = new HashMap<>();//一次最大查询200个数据,多个分页查询,这里做数据切割List<List<String>> splitList = DataCuttingUtil.dataCuttingString(itemIdList, ProductConstants.QUERY_ITEM_MAX_COUNT);for (List<String> itemIds : splitList) {LambdaQueryWrapper<ItemExpriResultBufferDO> queryWrapper = Wrappers.lambdaQuery();queryWrapper.in(ItemExpriResultBufferDO::getItemId, itemIds);queryWrapper.eq(ItemExpriResultBufferDO::getMarketExpriResult, MarketExpriResultEnum.UNDER_EXAMINATION.getCode());queryWrapper.eq(ItemExpriResultBufferDO::getDelFlag, YesOrNoEnum.YES.getCode());//分批次查询出符合条件的考核结果缓冲数据集合List<ItemExpriResultBufferDO> assessingBufferResultList = itemExpriResultBufferMapper.selectList(queryWrapper);if (!CollectionUtils.isEmpty(assessingBufferResultList)) {Map<String, ItemExpriResultBufferDTO> result = assessingBufferResultList.stream().map(itemExpriResultBufferDO -> itemExpriResultBufferConverter.entityToDTO(itemExpriResultBufferDO)).collect(Collectors.toMap(ItemExpriResultBufferDTO::getItemId, itemExpriResultBuffer -> itemExpriResultBuffer, (oldKey, newKey) -> oldKey));resultMap.putAll(result);}}return resultMap;}//批量插入考核缓冲结果数据public void insertBatch(List<ItemExpriResultBufferRequest> insertRequest) {List<ItemExpriResultBufferDO> bufferEntityList = itemExpriResultBufferConverter.listRequestToEntity(insertRequest);if (CollectionUtils.isEmpty(bufferEntityList)) {return;}Integer count = itemExpriResultBufferMapper.insertBatch(bufferEntityList);if (count <= 0) {throw new ProductBizException(CommonErrorCodeEnum.SQL_ERROR);}}...
}

步骤八:将参与本轮考核计算的考核指标数据都设置为失效状态

@Component
public class CalculateExpriResultSchedule {...//将参与本轮考核计算的考核指标数据都设置为失效状态//@param itemIdList 本轮考核指标数据对应的itemId集合private void batchInvalidExpriData(List<String> itemIdList) {productExpriRepository.invalidExpriDataResult(itemIdList);}...
}//商品考核资源管理
@Repository
public class ProductExpriRepository {...//将考核指标数据设置为失效状态public Boolean invalidExpriDataResult(List<String> itemIdList) {if (CollectionUtils.isEmpty(itemIdList)) {return false;}LambdaUpdateWrapper<ProductExpriDataResultDO> updateWrapper = Wrappers.lambdaUpdate();updateWrapper.in(ProductExpriDataResultDO::getItemId, itemIdList);updateWrapper.set(ProductExpriDataResultDO::getIsEffective, YesOrNoEnum.NO.getCode());int count = productExpriDataResultMapper.update(null, updateWrapper);if (count <= 0) {throw new ProductBizException(CommonErrorCodeEnum.SQL_ERROR);}return true;}...
}

(3)定时任务基于考核结果设置商品可采可补可售

步骤一:分页查每页1000条数据
步骤二:判断是否考核通过,通过的放在passList,失败的放在failList
步骤三:针对failList,设置供需状态,批量保存到考核结果表
步骤四:针对passList,判断所处生命周期,拆分成滞销期list,试销期list
步骤五:针对滞销期/试销期list,执行供需状态设置,保存到考核结果表
步骤六:针对passList发送考核通过消息,针对failList发送考核失败消息
步骤七:批量逻辑删除所有的考核通过/不通过的buffer数据
@Component
public class CalculateExpriResultSchedule {...//根据考核结果缓冲表中的考核状态和生命周期阶段,写入供需属性@XxlJob("calculateExpri")@Transactional(rollbackFor = Exception.class)public void calculateExpri() {//1.分页查,每页1000条数据//2.批量判断是否考核通过,通过的放在passList,失败的放在failList//3.针对failList的逻辑,执行对象转换,设置供需状态,批量保存到考核结果表//4.针对passList的逻辑,执行对象转换,判断所处生命周期,拆分成滞销期list,试销期list//5.针对滞销期/试销期list,执行供需状态设置,批量保存到考核结果表//6.批量针对passList发送考核通过消息,针对failList发送考核失败消息//7.批量逻辑删除所有的考核通过/不通过的buffer数据//查询出考核结果数据List<ItemExpriResultBufferDO> passExpriResultBufferList = getPassExpriResultBufferList();List<ItemExpriResultBufferDO> failExpriResultBufferList = getFailExpriResultBufferList();//批量处理考核成功结果List<ItemExpriResultDO> passedHandledList = batchHandlePassExpriResults(passExpriResultBufferList);//批量处理考核失败结果List<ItemExpriResultDO> failedHandledList = batchHandleFailExpriResults(failExpriResultBufferList);//批量分批次写入处理结果到考核表中batchSaveExpriResult(passedHandledList);batchSaveExpriResult(failedHandledList);//批量更新考核结果buffer数据为已失效itemExpriResultRepository.batchDeleteExpriBuffer(passExpriResultBufferList);itemExpriResultRepository.batchDeleteExpriBuffer(failExpriResultBufferList);}...
}

一.批量处理考核成功的结果

@Component
public class CalculateExpriResultSchedule {...//查询考核通过的buffer数据listprivate List<ItemExpriResultBufferDO> getPassExpriResultBufferList() {List<ItemExpriResultBufferDO> passExpriResultBufferList = new ArrayList<>(ProductConstants.QUERY_ITEM_MAX_COUNT * 10);Integer pageNum = 1;try {while (true) {//分页查询考核结果缓冲数据PageResult<ItemExpriResultBufferDO> page = itemExpriResultRepository.queryPassExpriResultBufferByPage(pageNum, ProductConstants.QUERY_ITEM_MAX_COUNT * 5);//内容为空时,跳出循环if (Objects.isNull(page) || CollectionUtils.isEmpty(page.getContent())) {break;}//查询到的结果集加入到通过list中passExpriResultBufferList.addAll(page.getContent());//页码+1pageNum += 1;Thread.sleep(20);}} catch (InterruptedException e) {throw new BaseBizException("根据考核结果更新供需状态job,查询缓冲数据执行异常");}return passExpriResultBufferList;}//考核成功结果处理供需属性private List<ItemExpriResultDO> batchHandlePassExpriResults(List<ItemExpriResultBufferDO> passExpriResultBufferList) {//批量处理考核成功结果List<String> itemIds = new ArrayList<>(passExpriResultBufferList.size());for (ItemExpriResultBufferDO resultBufferDO : passExpriResultBufferList) {//商品itemIdString itemId = resultBufferDO.getItemId();itemIds.add(itemId);}log.info("itemIds:{}", JSON.toJSONString(itemIds));//分页处理考核通过结果List<ItemPeriodStageDTO> stageDTOS = new ArrayList<>(passExpriResultBufferList.size());Integer fromIndex = 0;while (true) {//如果起始大于了list的结束位置,就退出循环if (fromIndex > passExpriResultBufferList.size() - 1) {break;}List<String> sbList = itemIds.subList(fromIndex, Math.min(fromIndex + ProductConstants.QUERY_ITEM_MAX_COUNT, passExpriResultBufferList.size()));//分批次更新商品前一次考核结果数据为失效itemExpriResultRepository.batchDeleteExpriResult(sbList);//分批查询商品生命周期所处阶段stageDTOS.addAll(itemPeriodStageRepository.queryPeriodStageByItemIds(new QueryPeriodListRequest(sbList)));fromIndex += ProductConstants.QUERY_ITEM_MAX_COUNT;}HashMap<String, Integer> itemStageMap = new HashMap(stageDTOS.size());//转换成map结构,避免后续处理的时候,多次遍历stageDTOS listfor (ItemPeriodStageDTO itemPeriodStageDTO : stageDTOS) {itemStageMap.put(itemPeriodStageDTO.getItemId(), itemPeriodStageDTO.getPeriodStage());}//循环设置考核结果供需属性List<ItemExpriResultDO> handledPassResults = new ArrayList<>(passExpriResultBufferList.size());for (ItemExpriResultBufferDO resultBufferDO : passExpriResultBufferList) {//生命周期阶段Integer periodStage = itemStageMap.get(resultBufferDO.getItemId());ItemExpriResultDO itemExpriResultDO = itemExpriResultConverter.converterBuffer(resultBufferDO);//试销期if (periodStage.equals(ProductStageEnum.TRY_SALE.getCode())) {//设置对象初始值itemExpriResultDO.setUpdateUser(1);itemExpriResultDO.initCommon();//设置试销期供需状态setTrySaleSupplyStatus(itemExpriResultDO);handledPassResults.add(itemExpriResultDO);}//成熟期if (periodStage.equals(ProductStageEnum.GROWN.getCode())) {//设置对象初始值itemExpriResultDO.setUpdateUser(1);itemExpriResultDO.initCommon();//设置成熟期供需状态setGrownSupplyStatus(itemExpriResultDO);handledPassResults.add(itemExpriResultDO);}}return handledPassResults;}//设置试销期供需状态private void setTrySaleSupplyStatus(ItemExpriResultDO itemExpriResultDO) {itemExpriResultDO.setPurchaseStatus(YesOrNoEnum.YES.getCode());//可采itemExpriResultDO.setReplenishmentStatus(YesOrNoEnum.YES.getCode());//可补itemExpriResultDO.setSaleStatus(YesOrNoEnum.YES.getCode());//可售}//设置成熟期供需状态private void setGrownSupplyStatus(ItemExpriResultDO itemExpriResultDO) {String itemId = itemExpriResultDO.getItemId();//商品状态Integer itemStatus = itemPeriodStageRepository.getItemStatus(itemId);//处于上架状态if (itemStatus.equals(ProductStatusEnum.PUT_ON_THE_MARKET.getCode())) {itemExpriResultDO.setPurchaseStatus(YesOrNoEnum.YES.getCode());//可采itemExpriResultDO.setReplenishmentStatus(YesOrNoEnum.YES.getCode());//可补itemExpriResultDO.setSaleStatus(YesOrNoEnum.YES.getCode());//可售return;}//不处于上架状态itemExpriResultDO.setPurchaseStatus(YesOrNoEnum.NO.getCode());//不可采itemExpriResultDO.setReplenishmentStatus(YesOrNoEnum.NO.getCode());//不可补itemExpriResultDO.setSaleStatus(YesOrNoEnum.NO.getCode());//不可售}...
}@Repository
public class ItemExpriResultRepository {...//分页查询考核通过数据集public PageResult<ItemExpriResultBufferDO> queryPassExpriResultBufferByPage(Integer pageNum, Integer pageSize) {LambdaQueryWrapper<ItemExpriResultBufferDO> queryWrapper = Wrappers.lambdaQuery();queryWrapper.eq(ItemExpriResultBufferDO::getMarketExpriResult, MarketExpriResultEnum.PASS_EXAMINATION.getCode());queryWrapper.eq(ItemExpriResultBufferDO::getDelFlag, YesOrNoEnum.YES.getCode());Page<ItemExpriResultBufferDO> page = new Page<>(pageNum, pageSize);return new PageResult<>(itemExpriResultBufferMapper.selectPage(page, queryWrapper));}...
}

二.批量处理考核失败的结果

@Component
public class CalculateExpriResultSchedule {...//查询考核失败的buffer数据listprivate List<ItemExpriResultBufferDO> getFailExpriResultBufferList() {List<ItemExpriResultBufferDO> passExpriResultBufferList = new ArrayList<>(ProductConstants.QUERY_ITEM_MAX_COUNT * 10);Integer pageNum = 1;try {while (true) {//分页查询考核结果缓冲数据PageResult<ItemExpriResultBufferDO> page = itemExpriResultRepository.queryFailExpriResultBufferByPage(pageNum, ProductConstants.QUERY_ITEM_MAX_COUNT * 5);//内容为空时,跳出循环if (Objects.isNull(page) || CollectionUtils.isEmpty(page.getContent())) {break;}//查询到的结果集加入到通过list中passExpriResultBufferList.addAll(page.getContent());//页码+1pageNum += 1;Thread.sleep(20);}} catch (InterruptedException e) {throw new BaseBizException("根据考核结果更新供需状态job,查询缓冲数据执行异常");}return passExpriResultBufferList;}//考核失败结果处理供需属性private List<ItemExpriResultDO> batchHandleFailExpriResults(List<ItemExpriResultBufferDO> failExpriResultBufferList) {//考核结果数据集,考核缓冲数据处理转换成考核结果数据集合,并批量保存至数据库List<ItemExpriResultDO> failExpriResultList = new ArrayList<>(failExpriResultBufferList.size());List<String> itemIds = new ArrayList<>(failExpriResultBufferList.size());//处理考核失败数据,设置淘汰期供需属性for (ItemExpriResultBufferDO resultBufferDO : failExpriResultBufferList) {//克隆缓存对象属性到考核结果对象ItemExpriResultDO itemExpriResultDO = itemExpriResultConverter.converterBuffer(resultBufferDO);itemExpriResultDO.setUpdateUser(1);itemExpriResultDO.initCommon();//设置淘汰期供需状态setPreEliminateSupplyStatus(itemExpriResultDO);failExpriResultList.add(itemExpriResultDO);//提取itemIditemIds.add(itemExpriResultDO.getItemId());}Integer fromIndex = 0;while (true) {//如果起始大于了list的结束位置,就退出循环if (fromIndex > failExpriResultBufferList.size() - 1) {break;}List<String> sbList = itemIds.subList(fromIndex, Math.min(fromIndex + ProductConstants.QUERY_ITEM_MAX_COUNT, failExpriResultBufferList.size()));//分批次更新商品前一次考核结果数据为失效itemExpriResultRepository.batchDeleteExpriResult(sbList);fromIndex += ProductConstants.QUERY_ITEM_MAX_COUNT;}return failExpriResultList;}//设置淘汰期供需状态private void setPreEliminateSupplyStatus(ItemExpriResultDO itemExpriResultDO) {itemExpriResultDO.setPurchaseStatus(YesOrNoEnum.NO.getCode());//不可采itemExpriResultDO.setReplenishmentStatus(YesOrNoEnum.NO.getCode());//不可补itemExpriResultDO.setSaleStatus(YesOrNoEnum.YES.getCode());//可售}//批量保存处理过的考核结果数据到考核结果private void batchSaveExpriResult(List<ItemExpriResultDO> handledList) {//分页写入考核失败结果至考核结果表Integer fromIndex = 0;try {while (true) {//判断下一次的list拆分是否超过了if (fromIndex > handledList.size() - 1) {break;}//每200条数据执行一次批量写List<ItemExpriResultDO> subList = handledList.subList(fromIndex, Math.min(fromIndex + ProductConstants.QUERY_ITEM_MAX_COUNT, handledList.size()));itemExpriResultRepository.saveItemExpriResultBatch(subList);//设置下一页的起始位置fromIndex += ProductConstants.QUERY_ITEM_MAX_COUNT;Thread.sleep(20);}} catch (InterruptedException e) {throw new BaseBizException("批量保存考核结果至考核结果表异常");}}...
}@Repository
public class ItemExpriResultRepository {...//分页查询考核不通过数据集public PageResult<ItemExpriResultBufferDO> queryFailExpriResultBufferByPage(Integer pageNum, Integer pageSize) {LambdaQueryWrapper<ItemExpriResultBufferDO> queryWrapper = Wrappers.lambdaQuery();queryWrapper.eq(ItemExpriResultBufferDO::getMarketExpriResult, MarketExpriResultEnum.FAIL_PASS_EXAMINATION.getCode());queryWrapper.eq(ItemExpriResultBufferDO::getDelFlag, YesOrNoEnum.YES.getCode());Page<ItemExpriResultBufferDO> page = new Page<>(pageNum, pageSize);return new PageResult<>(itemExpriResultBufferMapper.selectPage(page, queryWrapper));}//批量写入到考核结果表中public void saveItemExpriResultBatch(List<ItemExpriResultDO> list) {if (CollectionUtils.isEmpty(list)) {return;}int count = itemExpriResultMapper.saveBatch(list);if (count <= 0) {throw new BaseBizException(ProductExceptionCode.PRODUCT_SQL);}}//批量设置考核结果buffer数据为失效状态public void batchDeleteExpriBuffer(List<ItemExpriResultBufferDO> list) {if (CollectionUtils.isEmpty(list)) {return;}int count = itemExpriResultBufferMapper.batchDelete(list, YesOrNoEnum.NO.getCode());if (count <= 0) {throw new BaseBizException(ProductExceptionCode.PRODUCT_SQL);}}...
}

(4)考核结果数据的Excel导出

@RestController
@RequestMapping("/ItemPeriodStage")
public class ItemPeriodStageController {@Autowiredprivate ItemPeriodStageService itemPeriodStageService;...//导出数据到Excel@PostMapping("/exportItemExpriToExcel")public void exportExcel(@RequestBody ItemExpriReslutPageRequest request, HttpServletResponse response) throws IOException {try {List<ItemExpriResultToExcelDTO> itemExpriResults = itemPeriodStageService.queryItemExpriResultBatch(request);String title = "商品考核结果";String sheetName = title;String fileName = title + System.currentTimeMillis();log.info("result:{}", itemExpriResults);ExcelUtils.exportExcel(itemExpriResults, title, sheetName, ItemExpriResultToExcelDTO.class, fileName, response);} catch (BaseBizException baseBizException) {log.error("biz error: request={}", JSON.toJSONString(request), baseBizException);} catch (Exception e) {log.error("system error: request={}", JSON.toJSONString(request), e);}}...
}@Service
public class ItemPeriodStageServiceImpl implements ItemPeriodStageService {...//分页批量查询商品考核结果@Overridepublic List<ItemExpriResultToExcelDTO> queryItemExpriResultBatch(ItemExpriReslutPageRequest request) {//查询考核结果每次的数据量可能会比较大,所以预设一个较大的List初始大小,避免多次扩容List<ItemExpriResultDTO> results = new ArrayList<>(ProductConstants.QUERY_ITEM_MAX_COUNT * 10);//1.设置每次查询的数据量,最大为200request.setPageSize(ProductConstants.QUERY_ITEM_MAX_COUNT);Integer pageNum = request.getPageNum();List<ItemExpriResultDTO> batchResult;try {while (true) {//2.分页数据log.info("request:{}", JSON.toJSONString(request));PageResult<ItemExpriResultDTO> page = itemPeriodStageRepository.queryItemExpriResultByPage(request);//3.判断是否还有数据,还有数据则页码+1继续执行分页查询log.info("page:{}", JSON.toJSONString(page));if (Objects.isNull(page) || CollectionUtils.isEmpty(page.getContent())) {break;}//4.拿到本次查询的数据batchResult = page.getContent();log.info("dataList:{}", JSON.toJSONString(batchResult));//判断拿到的数据是否为空,或为null,若为空或为null,说明本次查询未查到数据,可直接breakif (CollectionUtils.isEmpty(batchResult)) {break;}//6.查询到的数据全部加入到返回的结果集中,页码+1results.addAll(batchResult);request.setPageNum(++pageNum);//每次循环获取数据后,休眠20ms,避免对数据库造成太大压力Thread.sleep(20);}} catch (InterruptedException e) {throw new BaseBizException("查询出错,request:{}", JSON.toJSONString(request));}log.info("result:{}", results);//7.返回转换后的结果集return transferItemExpriResultToExcelDTO(results);}...
}@Repository
public class ItemPeriodStageRepository {...//根据分页查询考核结果public PageResult<ItemExpriResultDTO> queryItemExpriResultByPage(ItemExpriReslutPageRequest request) {LambdaQueryWrapper<ItemExpriResultDO> queryWrapper = Wrappers.lambdaQuery();queryWrapper.eq(ItemExpriResultDO::getDelFlag, YesOrNoEnum.YES.getCode());//参数不为null,则设置为条件if (!Objects.isNull(request.getCategoryId())) {queryWrapper.eq(ItemExpriResultDO::getCategoryId, request.getCategoryId());}if (!Objects.isNull(request.getItemId())) {queryWrapper.eq(ItemExpriResultDO::getItemId, request.getItemId());}Page<ItemExpriResultDO> page = new Page<>(request.getPageNum(), request.getPageSize());Page<ItemExpriResultDO> skuPeriodStagePage = itemExpriResultMapper.selectPage(page, queryWrapper);return new PageResult<>(itemExpriResultConverter.converterDTOList(skuPeriodStagePage.getRecords()));}...
}//Excel导入导出工具类
public class ExcelUtils {...//excel 导出//@param list      数据列表//@param title     表格内数据标题//@param sheetName sheet名称//@param pojoClass pojo类型//@param fileName  导出时的excel名称//@param responsepublic static void exportExcel(List<?> list, String title, String sheetName, Class<?> pojoClass, String fileName, HttpServletResponse response) throws IOException {defaultExport(list, pojoClass, fileName, response, new ExportParams(title, sheetName, ExcelType.XSSF));}//excel 导出//@param list         数据列表//@param pojoClass    pojo类型//@param fileName     导出时的excel名称//@param response//@param exportParams 导出参数(标题、sheet名称、是否创建表头,表格类型)private static void defaultExport(List<?> list, Class<?> pojoClass, String fileName, HttpServletResponse response, ExportParams exportParams) throws IOException {//把表头、表格数据添加到excel表格中Workbook workbook = ExcelExportUtil.exportExcel(exportParams, pojoClass, list);//下载Excel到本地downLoadExcel(fileName, response, workbook);}//excel下载//@param fileName 下载时的文件名称//@param response//@param workbook excel数据private static void downLoadExcel(String fileName, HttpServletResponse response, Workbook workbook) throws IOException {try {response.setCharacterEncoding("UTF-8");response.setHeader("content-Type", "application/vnd.ms-excel");response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName + ".xlsx", "UTF-8"));workbook.write(response.getOutputStream());} catch (Exception e) {throw new IOException(e.getMessage());}}...
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/diannao/87184.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

鸿蒙组件通用事件开发全攻略:从基础交互到工程实践

一、引言&#xff1a;事件系统 —— 构建交互体验的核心枢纽 在鸿蒙应用开发体系中&#xff0c;组件事件系统是连接用户操作与应用逻辑的关键桥梁。从基础的点击交互到复杂的多触点手势&#xff0c;通用事件覆盖了全场景设备的交互需求。本文将系统解构鸿蒙事件体系的核心机制…

老项目重构难题破解:飞算 JavaAI 如何实现技术升级突围

在企业数字化转型进程中&#xff0c;大量 Java 老项目因长期迭代积累的技术债务&#xff0c;陷入 "重构必要性与实施难度并存" 的困境。这些遗留系统普遍存在代码体系老化、架构模式滞后、维护成本高企等问题&#xff0c;成为企业技术升级的绊脚石。 传统 Java 老项…

idea使用技巧分享

写在前面 分享一些常用的idea使用技巧&#xff0c;进来看看有没有你不知道的。 设置项目默认配置 TODO设置 位置 方式一&#xff1a;setting -> editor -> TODO 方式二&#xff1a; 定义Patterns过滤模式 正则中“\b”是元字符代表着单词的开头或结尾&#xff0c;也就…

【Dify精讲】第8章:Agent能力实现原理【知识卡片】

第8章&#xff1a;Agent能力实现原理http://www.airinto.com/share/e7b7e27f 一、Agent架构设计 二、工具调用机制 三、ReAct框架实现 四、自定义Agent开发 五、性能优化与监控 六、总结与实战建议

【软件】安装Miniconda

安装 根据搜索结果&#xff0c;以下是使用Homebrew在macOS上安装Miniconda的详细步骤&#xff1a; 1.安装Homebrew&#xff08;如果尚未安装&#xff09; 打开终端&#xff08;Terminal&#xff09;&#xff0c;运行以下命令安装Homebrew&#xff1a; /bin/bash -c "$(…

FastAPI:(6)错误处理

FastAPI&#xff1a;(6)错误处理 由于CSDN无法展示「渐构」的「#d&#xff0c;#e&#xff0c;#t&#xff0c;#c&#xff0c;#v&#xff0c;#a」标签&#xff0c;推荐访问我个人网站进行阅读&#xff1a;Hkini 「渐构展示」如下&#xff1a; #c 概述 文章概念关系 graph TDA…

408第一季 - 数据结构 - 排序

排序的概念 外部排序很难&#xff0c;后面都是内部排序 插入排序 直接插入排序 理解 这个排序第一轮是从第二个元素开始的 然后是从后往前一个一个比的 然后我们看i5的情况&#xff0c;会出现比较次数和移动次数的概念&#xff0c;这里97动了 然后i8时&#xff0c;49最好…

高效账号信息管理工具,可安全随机生成密码

软件介绍 今天给大家推荐一款安全可靠的密码管理工具&#xff0c;帮助用户轻松管理各类账号密码。 安全便捷的密码解决方案 这是一款采用先进加密技术开发的密码管理器&#xff0c;不仅可以生成高强度随机密码&#xff0c;还提供安全的账号密码备份存储功能。 基础安全设置 …

如何在markdown文件中(博客)添加emoji表情,让你的博客看起来更加优雅

在Markdown中使用Emoji的完整指南 按分类快速参考的完整Emoji列表一、状态指示类:bulb:二、提示信息类:bulb:三、内容类型类:bulb:四、操作指令类:bulb:五、进度状态类:bulb:六、技术相关类:bulb:七、人员角色类:bulb:八、版本控制类:bulb: 你学会了吗 按分类快速参考的完整Emo…

MAZANOKE:一款隐私优先的浏览器图像优化工具及Docker部署指南

在日常工作中&#xff0c;大家是否经常遇到这样的需求&#xff1a;需要压缩图片体积、调整图片尺寸或转换图片格式&#xff0c;但又受限于数据安全要求无法将图片上传至公网&#xff1f;在我们之前开发的工单配置系统中&#xff0c;这类需求尤为常见。最近在GitHub上发现了一款…

【Vue PDF】Vue PDF 组件初始不加载 pdfUrl 问题分析与修复

Vue PDF 组件初始不加载 pdfUrl 问题分析与修复 问题现象 在开发 PDF 预览组件时&#xff0c;遇到这样一个问题&#xff1a; 初始状态下&#xff0c;PDF 组件不会请求 pdfUrl&#xff08;即不会加载 PDF 文件&#xff09;。只有点击"全屏"按钮后&#xff0c;才会请…

《注解的江湖:一场元数据的“宫斗剧”》

一、你真的懂注解吗 你是否使用过Autowired却不知道是如何生效的&#xff1f; 这几个注解你一定很熟悉&#xff1a; OverrideDeprecatedTransactional 那么你有进一步思考过怎么生效的吗&#xff1f;注解到底是什么&#xff1f;注解&#xff0c;到底是信息&#xff1f;还是指…

智能土木通 - 土木工程专业知识问答系统02-RAG检索模块搭建

一、项目目录 civil_qa_system/ ├── docs/ # 项目文档 ├── config/ # 配置文件 ├── core/ # 核心功能代码 ├── knowledge_base/ # 知识库相关 ├── web/ # Web应用部分 ├…

进程和线程区别、管道和套接字、共享变量、TCP三次握手,是否可以少一次握手、子进程和主进程区别和API——Nodejs

首先讲了进程和线程区别 然后讲解 管道和套接字&#xff0c;它是进程间通信的方式 接着讲解共享变量 &#xff0c;它是线程间通信 最后讲解TCP三次握手&#xff0c;因为套接字使用了TCP协议 一、线程和进程的区别 线程&#xff08;Thread&#xff09;和进程&#xff08;Pr…

docker(学习笔记第一课) 使用nginx +https + wordpress

文章目录 docker(学习笔记第一课) 使用nginx https wordpress学习内容&#xff1a;1. 整体架构1.1 在aws ec2的整体架构1.2 不懂都可以问AI 2. 构建详细2.1 构建ec22.2 安装docker2.3 创建一个docker的内部network2.4 创建wordpress使用的mysql数据库2.5 创建两个wordpress的d…

Leetcode 刷题记录 15 —— 二分查找

本系列为笔者的 Leetcode 刷题记录&#xff0c;顺序为 Hot 100 题官方顺序&#xff0c;根据标签命名&#xff0c;记录笔者总结的做题思路&#xff0c;附部分代码解释和疑问解答&#xff0c;01~07为C语言&#xff0c;08及以后为Java语言。 01 搜索插入位置 class Solution {pub…

C++核心编程(动态类型转换,STL,Lanmda)

一. 类型转换 二. STL 1. 容器 1.1 Vector&#xff08;常用&#xff09; 1.1.1 概述 特性&#xff1a; 动态数组&#xff1a; 想象成一个会自动变长变短的数组。起始在内存中是连续存储的。 随机访问&#xff1a; 通过[]运算符或at()方法&#xff0c;可以瞬间&#xff08;…

【图像处理入门】8. 数学基础与优化:线性代数、概率与算法调优实战

摘要 图像处理的核心离不开数学工具的支撑。本文将深入解析线性代数、概率论在图像领域的应用,包括矩阵变换与图像几何操作的关系、噪声模型的数学描述,以及遗传算法、粒子群优化等智能算法在参数调优中的实践。通过理论结合代码案例,帮助读者掌握从数学原理到工程优化的完…

操作系统八股文

一.进程和线程的区别 1.本质区别和所属关系是什么&#xff1f; 进程是资源调度以及分配的基本单位。 线程是CPU调度的基本单位。 一个线程属于一个进程&#xff0c;一个进程可以拥有多个线程。 2.地址空间和内存 进程拥有独立的虚拟地址空间。 线程没有独立的地址空间&#xf…

【uniapp】小程序中input输入框的placeholder-class不生效

解决方法 1.去掉scoped <style></style> 2.额外写一组style </style lang"scss" scoped> </style> <style> ::v-deep .textarea-placeholder { font-size: 24rpx; font-weight: 400; …