SpringBoot项目使用EntityManager进行报表统计

SpringBoot项目使用EntityManager进行报表统计

当前实现缺陷,如果待统计的数据过多,可能会OOM,分页应该直接在构造的SQL语句中体现,而不是拿回所有数据再分页

如果您统计的数量较少,可以稍加调整进行数据统计,如果数据量较大,分页查询!构造分页SQL!!!


/**
 * @Author: Be_insghted
 * Description:
 * @date Create on 2020/6/15 15:45
 **/
@Service
@Slf4j
public class StaticRecordService {

    @Autowired
    private RecipeCookTaskDao cookTaskDao;
    @Autowired
    private EntityManager entityManager;
    @Autowired
    private RecipeService recipeService;
    @Autowired
    private BusinessCloudConfig businessCloudConfig;
    @Autowired
    private ShopServer shopServer;
    @Autowired
    private AdminTokenService tokenService;
    @Autowired
    private IotProductKey iotProductKey;
    @Autowired
    private RecipeServer recipeServer;

    @Autowired
    private RecipeCookTaskEntityMapper recipeCookTaskEntityMapper;

    private static final String TASK_ADD_TIME_DEVICE = "addTime";

    /**
     * All storeNames are for candidate collection
     */
    private List<String> storeNames = new CopyOnWriteArrayList<>();

    /**
     * All storeId matched storeName、storeId、member_id、member_name collection
     */
    private Map<String, MerchantInfo2StoreInfoDto> storeId2StoreMainInfoMap = new ConcurrentHashMap<>();


    public void setStoreId2StoreMainInfoMap(Map<String, MerchantInfo2StoreInfoDto> storeId2StoreMainInfoMap) {
        this.storeId2StoreMainInfoMap = storeId2StoreMainInfoMap;
    }
    public void setStoreNames(List<String> storeNames) {
        this.storeNames = storeNames;
    }


    public Page<RecordMerchantVO> recipeCookTaskEntityMapper(QueryRecordReq pageInfo) {
        PageHelper.startPage(pageInfo.getPage(), pageInfo.getSize());
        List<RecordMerchantVO> list = recipeCookTaskEntityMapper.selectMerchantCookTaskTotal(pageInfo);
        return new PageImpl<>(list, new PageRequest(pageInfo.getPage() - 1, pageInfo.getSize()), ((com.github.pagehelper.Page) list).getTotal());
    }

    /**
     * 烹饪记录: 根据条件查询商家维度
     *
     * @param req
     */
    public Page<RecordMerchantVO> queryMerchantRecord(QueryRecordReq req) {
        String sortWord = req.getSortWord();
        String direction = req.getDirection();
        Sort sort = new Sort(Sort.Direction.DESC, TASK_ADD_TIME_DEVICE);
        Pageable pageable = new PageRequest(req.getPage() - 1, req.getSize(), sort);
        if (StringUtils.isNotBlank(req.getStoreName()) || StringUtils.isNotBlank(req.getMerchantId()) || StringUtils.isNotBlank(req.getStoreId())) {
            String storeId = getStoreId(req.getStoreId(), req.getMerchantId(), req.getStoreName());
            if (StringUtils.isEmpty(storeId)) {
                return new PageImpl<>(new ArrayList<>(), pageable, 0);
            }
            req.setStoreId(storeId);
        }

        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        //查询店铺名称、账户,统计店铺总烹饪的次数、 失败(取消+失败)菜谱数、菜譜烹飪成功次数、商家使用拥有菜谱数、设备使用天数

        CriteriaQuery<RecordMerchantResult> criteriaQuery = cb.createQuery(RecordMerchantResult.class);

        Root<RecipeCookTask> root = criteriaQuery.from(RecipeCookTask.class);
        criteriaQuery.multiselect(root.get("storeId"),
                cb.count(cb.selectCase().when(root.get("status").in(Arrays.asList(RecipeCookTask.STATUS.FAILURE, RecipeCookTask.STATUS.CANCEL)), 1).otherwise(cb.nullLiteral(Number.class))),
                cb.count(cb.selectCase().when(root.get("status").in(Arrays.asList(RecipeCookTask.STATUS.FINISHED)), 1).otherwise(cb.nullLiteral(Number.class))),
                cb.count(root.get("storeId")),
                cb.countDistinct((root.get("taskDate"))),
                cb.countDistinct(root.get("recipeId"))
        );

        List<Predicate> predicateList = new ArrayList<>();

        if (StringUtils.isNotBlank(req.getMerchantId())) {
            // TODO 获取商家的店铺列表 -- storeIds 模拟
            List<String> storeIds = Arrays.asList("304108006227476480", "283491778840092672", "306209606672760832", "392849008856612864", "393998821044756480");
            if (null != storeIds) {
                predicateList.add(root.get("storeId").in(storeIds));
            }

        }
        if (req.getStartTime() != null && req.getEndTime() != null) {
            predicateList.add(cb.greaterThanOrEqualTo(root.get(TASK_ADD_TIME_DEVICE).as(Long.class), req.getStartTime()));
            predicateList.add(cb.lessThanOrEqualTo(root.get(TASK_ADD_TIME_DEVICE).as(Long.class), req.getEndTime()));
        }
        Predicate[] p = new Predicate[predicateList.size()];
        criteriaQuery.where(cb.and(predicateList.toArray(p)));
        criteriaQuery.groupBy(root.get("storeId"));
        List<RecordMerchantResult> counts = entityManager.createQuery(criteriaQuery).getResultList();
        counts = sortByProperty(counts, direction, sortWord);

        final List<RecordMerchantResult> list = counts;
        final List<List<RecordMerchantResult>> partition = Lists.partition(list, pageable.getPageSize());
        List<RecordMerchantResult> pageContent;
        if (CollectionUtils.isEmpty(list)) {
            return new PageImpl<>(new ArrayList<>(0), pageable, 0);
        } else {
            pageContent = partition.get(pageable.getPageNumber());
        }
        List<RecordMerchantVO> vos = convertResult2VO(pageContent);
        PageImpl<RecordMerchantVO> pageData = new PageImpl<>(vos, pageable, counts.size());
        return pageData;
    }

    public long countMerchantRecords(QueryRecordReq req) {
        if (StringUtils.isNotBlank(req.getStoreName()) || StringUtils.isNotBlank(req.getMerchantId()) || StringUtils.isNotBlank(req.getStoreId())) {
            String storeId = getStoreId(req.getStoreId(), req.getMerchantId(), req.getStoreName());
            if (StringUtils.isEmpty(storeId)) {
                return 0L;
            }
            req.setStoreId(storeId);
        }

        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        //查询店铺名称、账户,统计店铺总烹饪的次数、 失败(取消+失败)菜谱数、菜譜烹飪成功次数、商家使用拥有菜谱数、设备使用天数
        CriteriaQuery<RecordMerchantResult> criteriaQuery = cb.createQuery(RecordMerchantResult.class);
        Root<RecipeCookTask> root = criteriaQuery.from(RecipeCookTask.class);
        criteriaQuery.multiselect(root.get("storeId"),
                cb.count(cb.selectCase().when(root.get("status").in(Arrays.asList(RecipeCookTask.STATUS.FAILURE, RecipeCookTask.STATUS.CANCEL)), 1).otherwise(cb.nullLiteral(Number.class))),
                cb.count(cb.selectCase().when(root.get("status").in(Arrays.asList(RecipeCookTask.STATUS.FINISHED)), 1).otherwise(cb.nullLiteral(Number.class))),
                cb.count(root.get("storeId")),
                cb.countDistinct((root.get("taskDate"))),
                cb.countDistinct(root.get("recipeId"))
        );

        List<Predicate> predicateList = new ArrayList<>();
        if (!StringUtils.isEmpty(req.getStoreId())) {
            predicateList.add(cb.equal(root.get("storeId").as(String.class), req.getStoreId()));
        }
        if (req.getStartTime() != null && req.getEndTime() != null) {
            predicateList.add(cb.greaterThanOrEqualTo(root.get("addTime").as(Long.class), req.getStartTime()));
            predicateList.add(cb.lessThanOrEqualTo(root.get("addTime").as(Long.class), req.getEndTime()));
        }
        Predicate[] p = new Predicate[predicateList.size()];
        criteriaQuery.where(cb.and(predicateList.toArray(p)));
        criteriaQuery.groupBy(root.get("storeId"));
        List<RecordMerchantResult> counts = entityManager.createQuery(criteriaQuery).getResultList();
        final List<RecordMerchantResult> list = counts;
        if (CollectionUtils.isEmpty(list)) {
            return 0L;
        }
        return counts.size();

    }

    /**
     * 菜谱维度
     * @param pageInfo
     * @return
     */
    public Page<RecordRecipeVO> recipeCookTaskMerchantList(QueryRecipeReq pageInfo) {
        PageHelper.startPage(pageInfo.getPage(), pageInfo.getSize());
        List<RecordRecipeVO> list = new ArrayList<>();
        if (StringUtils.isBlank(pageInfo.getStoreId())
                && StringUtils.isBlank(pageInfo.getStoreName())
                && StringUtils.isBlank(pageInfo.getBindMerchantId())
                && StringUtils.isBlank(pageInfo.getBindMerchantName())) {
            list = recipeCookTaskEntityMapper.selectRecipeCookTaskTotal2(pageInfo);
        } else {
            list = recipeCookTaskEntityMapper.selectRecipeCookTaskTotal(pageInfo);
        }

        if(CollectionUtils.isNotEmpty(list)){
            // 获取当前页数据的创作方、当前同步请求
            List<String> recipeIds = list.stream().distinct().map(RecordRecipeVO::getRecipeId).collect(Collectors.toList());
            Map<String, String> recipeId2RecipeCreator = recipeServer.listByRecipeIds(recipeIds).getData().stream().collect(Collectors.toMap(StoreRecipeSimpleVo::getRecipeId, StoreRecipeSimpleVo::getPublishStoreName));
            for(RecordRecipeVO vo : list){
                if (recipeId2RecipeCreator.keySet().contains(vo.getRecipeId())) {
                    vo.setRecipeCreator(recipeId2RecipeCreator.get(vo.getRecipeId()));
                }
            }
        }
        return new PageImpl<>(list, new PageRequest(pageInfo.getPage() - 1, pageInfo.getSize()), ((com.github.pagehelper.Page) list).getTotal());
    }


    /**
     * 菜谱维度明细
     * @param pageInfo
     * @return
     */
    public Page<RecordRecipeDetailVO> selectRecipeCookTaskDetail(QueryRecipeReq pageInfo) {
        PageHelper.startPage(pageInfo.getPage(), pageInfo.getSize());
        List<RecordRecipeDetailVO> list = recipeCookTaskEntityMapper.selectRecipeCookTaskDetail(pageInfo);
        if (CollectionUtils.isNotEmpty(list)) {
            for (RecordRecipeDetailVO vo : list) {
                if (vo.getTimeConsumed() < 0) {
                    vo.setTimeConsumed(0);
                }
            }
        }
        return new PageImpl<>(list, new PageRequest(pageInfo.getPage() - 1, pageInfo.getSize()), ((com.github.pagehelper.Page) list).getTotal());
    }


    /**
     *
     * @param pageInfo
     * @return
     */
    public Page<RecordDeviceVO> selectRecipeDevicesTaskDetail(QueryDeviceReq pageInfo) {
        PageHelper.startPage(pageInfo.getPage(), pageInfo.getSize());
        List<RecordDeviceVO> list = recipeCookTaskEntityMapper.selectRecipeDevicesTaskDetail(pageInfo);
        return new PageImpl<>(list, new PageRequest(pageInfo.getPage() - 1, pageInfo.getSize()), ((com.github.pagehelper.Page) list).getTotal());
    }




    /**
     * 转换sql查询结果成VO
     *
     * @param pageContent
     * @return
     */
    public List<RecordMerchantVO> convertResult2VO(List<RecordMerchantResult> pageContent) {
        List<RecordMerchantVO> vos = new ArrayList<>(pageContent.size());
        RecordMerchantResult tmp;
        for (int i = 0; i < pageContent.size(); i++) {
            RecordMerchantVO recordMerchantVO = new RecordMerchantVO();
            tmp = pageContent.get(i);
            recordMerchantVO.setCuisineCount(tmp.getCuisineCount())
                    .setErrorCuisineCount(tmp.getErrorCuisineCount())
                    .setStoreId(tmp.getStoreId())
                    .setSuccessCuisineCount(tmp.getSuccessCuisineCount())
                    .setDeviceUsageDays(tmp.getDeviceUsageDays())
                    .setRecipeUsedCount(tmp.getRecipeUsedCount());
            if (storeId2StoreMainInfoMap.keySet().contains(tmp.getStoreId())) {
                recordMerchantVO.setStoreName(storeId2StoreMainInfoMap.get(tmp.getStoreId()).getStoreName())
                        .setMerchantId(storeId2StoreMainInfoMap.get(tmp.getStoreId()).getMerchantId())
                        .setMerchantName(storeId2StoreMainInfoMap.get(tmp.getStoreId()).getMerchantName());
            }
            vos.add(recordMerchantVO);
        }
        return vos;
    }

    /**
     * 排序处理
     *
     * @param counts
     * @param property
     * @param sortWord
     * @return
     */
    public List<RecordMerchantResult> sortByProperty(List<RecordMerchantResult> counts, String property, String sortWord) {

        // 降序字段
        if (StringUtils.equals(SortDirectionConst.DESC, property)) {
            if (StringUtils.equals(sortWord, SortWord.CUISINE_COUNT)) {
                counts = counts.stream().sorted(Comparator.comparing(RecordMerchantResult::getCuisineCount).reversed()).collect(Collectors.toList());
            }
            if (StringUtils.equals(sortWord, SortWord.CUISINE_ERROR)) {
                counts = counts.stream().sorted(Comparator.comparing(RecordMerchantResult::getErrorCuisineCount).reversed()).collect(Collectors.toList());
            }
            if (StringUtils.equals(sortWord, SortWord.CUISINE_SUCCESS)) {
                counts = counts.stream().sorted(Comparator.comparing(RecordMerchantResult::getSuccessCuisineCount).reversed()).collect(Collectors.toList());
            }
            if (StringUtils.equals(sortWord, SortWord.DEVICE_USAGE_DAYS)) {
                counts = counts.stream().sorted(Comparator.comparing(RecordMerchantResult::getDeviceUsageDays).reversed()).collect(Collectors.toList());
            }
            if (StringUtils.equals(sortWord, SortWord.CUISINE_RECIPES)) {
                counts = counts.stream().sorted(Comparator.comparing(RecordMerchantResult::getRecipeUsedCount).reversed()).collect(Collectors.toList());
            }
        }

        // 升序字段
        if (StringUtils.equals(SortDirectionConst.ASC, property)) {
            if (StringUtils.equals(sortWord, SortWord.CUISINE_COUNT)) {
                counts = counts.stream().sorted(Comparator.comparing(RecordMerchantResult::getCuisineCount)).collect(Collectors.toList());
            }
            if (StringUtils.equals(sortWord, SortWord.CUISINE_ERROR)) {
                counts = counts.stream().sorted(Comparator.comparing(RecordMerchantResult::getErrorCuisineCount)).collect(Collectors.toList());
            }
            if (StringUtils.equals(sortWord, SortWord.CUISINE_SUCCESS)) {
                counts = counts.stream().sorted(Comparator.comparing(RecordMerchantResult::getSuccessCuisineCount)).collect(Collectors.toList());
            }
            if (StringUtils.equals(sortWord, SortWord.DEVICE_USAGE_DAYS)) {
                counts = counts.stream().sorted(Comparator.comparing(RecordMerchantResult::getDeviceUsageDays)).collect(Collectors.toList());
            }
            if (StringUtils.equals(sortWord, SortWord.CUISINE_RECIPES)) {
                counts = counts.stream().sorted(Comparator.comparing(RecordMerchantResult::getRecipeUsedCount)).collect(Collectors.toList());
            }
        }
        return counts;

    }


    /**
     * use native query
     */
    public List<RecordMerchantBackVO> nativeQuery() {
        String sql = "select count(1) AS total,\n" +
                "sum(case finish_time when 0 then 1 else 0 end) AS success,\n" +
                "sum(case finish_time when 0 then 0 else 1 end) AS failure,\n" +
                "merchant_id, store_id,\n" +
                "COUNT(DISTINCT DATE(task_date)) AS days\n" +
                "from recipe_cook_task\n" +
                "group by merchant_id, store_id";
        Query query = entityManager.createNativeQuery(sql);
        //query.unwrap(SQLQuery.class).setResultTransformer(Transformers.aliasToBean(RecordMerchantBackVO.class));
        query.unwrap(SQLQuery.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        List list = query.getResultList();
        return list;
    }

    /**
     * 烹飪記錄——菜譜維度
     *
     * @param req edited by Liubo at 20200810
     *            Description: add column recipe creator for exhibition
     * @return
     */
    public Page<RecordRecipeVO> queryRecipeRecord(QueryRecipeReq req) {
        //新建一个页面,存放页面信息
        Sort sort = new Sort(Sort.Direction.DESC, TASK_ADD_TIME_DEVICE);
        Pageable page = new PageRequest(req.getPage() - 1, req.getSize(), sort);
        if (StringUtils.isNotBlank(req.getStoreName()) || StringUtils.isNotBlank(req.getStoreId()) || StringUtils.isNotBlank(req.getMerchantId())) {
            String storeId = getStoreId(req.getStoreId(), req.getMerchantId(), req.getStoreName());
            if (StringUtils.isEmpty(storeId)) {
                return new PageImpl<>(new ArrayList<>(), page, 0);
            }
            req.setStoreId(storeId);
        }
        String sortWord = req.getSortWord();
        String direction = req.getDirection();

        CriteriaBuilder cb = entityManager.getCriteriaBuilder();

        CriteriaQuery<RecordRecipeResult> criteriaQuery = cb.createQuery(RecordRecipeResult.class);

        Root<RecipeCookTask> root = criteriaQuery.from(RecipeCookTask.class);

        criteriaQuery.multiselect(
                root.get("recipeName"),
                root.get("recipeId"),
                root.get("productKey"),
                cb.count(cb.selectCase().when(root.get("status").in(Arrays.asList(RecipeCookTask.STATUS.FAILURE, RecipeCookTask.STATUS.CANCEL)), 1).otherwise(cb.nullLiteral(Number.class))),
                cb.count(root.get("status")),
                cb.countDistinct(root.get("storeId")));
        List<Predicate> predicateList = new ArrayList<>();
        if (!StringUtils.isEmpty(req.getRecipeId())) {
            predicateList.add(cb.equal(root.get("recipeId").as(String.class), req.getRecipeId()));
        }
        if (!StringUtils.isEmpty(req.getRecipeName())) {
            predicateList.add(cb.like(root.get("recipeName").as(String.class), "%" + req.getRecipeName().trim() + "%"));
        }
        if (!StringUtils.isEmpty(req.getStoreId())) {
            predicateList.add(cb.equal(root.get("storeId").as(String.class), req.getStoreId()));
        }
        // if (!StringUtils.isEmpty(req.getMerchantId())) {
        //     predicateList.add(cb.equal(root.get("merchantId").as(String.class), req.getMerchantId().trim() ));
        // }
        if (req.getStartTime() != null && req.getEndTime() != null) {

            predicateList.add(cb.greaterThanOrEqualTo(root.get("addTime").as(Long.class), req.getStartTime()));
            predicateList.add(cb.lessThanOrEqualTo(root.get("addTime").as(Long.class), req.getEndTime()));
        }
        Predicate[] p = new Predicate[predicateList.size()];
        criteriaQuery.where(cb.and(predicateList.toArray(p)));
        // groupBy
        criteriaQuery.groupBy(root.get("recipeId"), root.get("recipeName"), root.get("productKey"));

        //记录当前sql查询结果总条数
        List<RecordRecipeResult> counts = entityManager.createQuery(criteriaQuery).getResultList();
        if (CollectionUtils.isEmpty(counts)) {
            return new PageImpl<>(new ArrayList<>(0), page, 0);
        }
        counts = recordRecipeResultsSort(counts, direction, sortWord);

        final List<RecordRecipeResult> list = counts;
        final List<List<RecordRecipeResult>> partition = Lists.partition(list, page.getPageSize());
        List<RecordRecipeResult> pageContent = partition.get(page.getPageNumber());
        log.info("CuisineRecordService#queryRecipeRecord 当前页{}<——共{}个菜谱", page, pageContent.size());
        // 获取当前页数据的创作方、当前同步请求
        List<String> recipeIds = pageContent.stream().distinct().map(RecordRecipeResult::getRecipeId).collect(Collectors.toList());
        Map<String, String> recipeId2RecipeCreator = recipeServer.listByRecipeIds(recipeIds).getData().stream().collect(Collectors.toMap(StoreRecipeSimpleVo::getRecipeId, StoreRecipeSimpleVo::getPublishStoreName));

        String storeId = req.getStoreId();
        String storeName = null;
        String merchantId = null;
        String merchantName = "";
        if (StringUtils.isNotEmpty(storeId) && storeId2StoreMainInfoMap.keySet().contains(storeId)) {
            storeName = storeId2StoreMainInfoMap.get(storeId).getStoreName();
            merchantId = storeId2StoreMainInfoMap.get(storeId).getMerchantId();
            merchantName = storeId2StoreMainInfoMap.get(storeId).getMerchantName();
        }
        List<RecordRecipeVO> vos = new ArrayList<>();
        for (int i = 0; i < pageContent.size(); i++) {
            RecordRecipeResult tmp = pageContent.get(i);
            RecordRecipeVO vo = new RecordRecipeVO();
            vo.setStoreCount(tmp.getStoreCount())
                    .setRecipeId(tmp.getRecipeId())
                    .setCuisineCount(tmp.getCuisineCount())
                    .setProductKey(tmp.getProductKey())
                    .setErrorCuisineCount(tmp.getErrorCuisineCount())
                    .setRecipeName(tmp.getRecipeName());
            if (StringUtils.isNotEmpty(storeId)) {
                vo.setMerchantId(merchantId)
                        .setStoreId(storeId)
                        .setStoreName(storeName)
                        .setMerchantName(merchantName);
            }
            if (recipeId2RecipeCreator.keySet().contains(tmp.getRecipeId())) {
                vo.setRecipeCreator(recipeId2RecipeCreator.get(tmp.getRecipeId()));
            }

            vos.add(vo);
        }

        //返回查询的分页结果,createQuery.getResultList()为分页查询的结果对象,counts.size()为设置分页参数之前查询的总数
        return new PageImpl<>(vos, page, counts.size());
    }

    /**
     * 菜譜維度 —— 排序
     *
     * @param counts
     * @param direction
     * @param sortWord
     * @return
     */
    List<RecordRecipeResult> recordRecipeResultsSort(List<RecordRecipeResult> counts, String direction, String sortWord) {

        if (StringUtils.equals(SortDirectionConst.DESC, direction)) {
            if (StringUtils.equals(SortWord.CUISINE_COUNT, sortWord)) {
                counts = counts.stream().sorted(Comparator.comparing(RecordRecipeResult::getCuisineCount).reversed()).collect(Collectors.toList());
            }
            if (StringUtils.equals(sortWord, SortWord.CUISINE_ERROR)) {
                counts = counts.stream().sorted(Comparator.comparing(RecordRecipeResult::getErrorCuisineCount).reversed()).collect(Collectors.toList());
            }
            if (StringUtils.equals(sortWord, SortWord.STORE_COUNT)) {
                counts = counts.stream().sorted(Comparator.comparing(RecordRecipeResult::getStoreCount).reversed()).collect(Collectors.toList());
            }
        }

        if (StringUtils.equals(SortDirectionConst.ASC, direction)) {
            if (StringUtils.equals(SortWord.CUISINE_COUNT, sortWord)) {
                counts = counts.stream().sorted(Comparator.comparing(RecordRecipeResult::getCuisineCount)).collect(Collectors.toList());
                log.info("排序后ASC{}", counts.stream().map(RecordRecipeResult::getCuisineCount).collect(Collectors.toList()));
            }
            if (StringUtils.equals(sortWord, SortWord.CUISINE_ERROR)) {
                counts = counts.stream().sorted(Comparator.comparing(RecordRecipeResult::getErrorCuisineCount)).collect(Collectors.toList());
            }
            if (StringUtils.equals(sortWord, SortWord.STORE_COUNT)) {
                counts = counts.stream().sorted(Comparator.comparing(RecordRecipeResult::getStoreCount)).collect(Collectors.toList());
            }
        }

        return counts;
    }


    /**
     * 烹飪記錄——菜譜明細維度
     *
     * @param req
     * @return
     */
    public Page<RecordRecipeDetailVO> queryRecipeDetailRecord(QueryRecipeReq req) {
        Sort sort = new Sort(Sort.Direction.DESC, TASK_ADD_TIME_DEVICE);
        if (SortDirectionConst.ASC.equals(req.getDirection()) && req.getSortWord().equals(SortWord.COOK_TIME)) {
            sort = new Sort(Sort.Direction.ASC, SortWord.COOK_TIME);
        }
        if (SortDirectionConst.DESC.equals(req.getDirection()) && req.getSortWord().equals(SortWord.COOK_TIME)) {
            sort = new Sort(Sort.Direction.DESC, SortWord.COOK_TIME);
        }
        if (StringUtils.isBlank(req.getDirection()) && StringUtils.isBlank(req.getSortWord())) {
            sort = new Sort(Sort.Direction.DESC, TASK_ADD_TIME_DEVICE);
        }
        Pageable pageable = new PageRequest(req.getPage() - 1, req.getSize(), sort);
        if (StringUtils.isNotBlank(req.getStoreId()) || StringUtils.isNotBlank(req.getMerchantId()) || StringUtils.isNotBlank(req.getStoreName())) {
            String storeId = getStoreId(req.getStoreId(), req.getMerchantId(), req.getStoreName());
            if (StringUtils.isEmpty(storeId)) {
                return new PageImpl<>(new ArrayList<>(), pageable, 0);
            }
            req.setStoreId(storeId);
        }
        Page<RecipeCookTask> pageData = cookTaskDao.findAll(getRecipeCookTaskSpecification(req), pageable);
        List<RecipeCookTask> list = pageData.getContent();
        if (CollectionUtils.isEmpty(list)) {
            return new PageImpl<>(new ArrayList<>(0), pageable, 0);
        }
        List<RecordRecipeDetailVO> vos = convertDBResult2RecipeDetailVO(list);
        if (SortDirectionConst.ASC.equals(req.getDirection()) && req.getSortWord().equals(SortWord.TIME_CONSUMED)) {
            Integer srcsize = req.getSize();
            Integer srcpage = req.getPage();
            long total = pageData.getTotalElements();
            int size = (int) total;
            req.setSize(size);
            req.setPage(1);
            pageable = new PageRequest(req.getPage() - 1, size);
            pageData = cookTaskDao.findAll(getRecipeCookTaskSpecification(req), pageable);
            List<RecipeCookTask> content = pageData.getContent();
            vos = convertDBResult2RecipeDetailVO(content);
            vos = vos.stream().sorted(Comparator.comparingLong(RecordRecipeDetailVO::getTimeConsumed)).collect(Collectors.toList());
            log.info("排序后{}", vos.stream().map(RecordRecipeDetailVO::getTimeConsumed).collect(Collectors.toList()));
            Page<RecordRecipeDetailVO> rets = ConvertList2PageVOUtils.convertList2PageVO(vos, srcpage, srcsize);
            return new PageImpl<>(rets.getContent(), pageable, pageData.getTotalElements());

        }

        if (SortDirectionConst.DESC.equals(req.getDirection()) && req.getSortWord().equals(SortWord.TIME_CONSUMED)) {
            Integer srcsize = req.getSize();
            Integer srcpage = req.getPage();
            long total = pageData.getTotalElements();
            int size = (int) total;
            req.setSize(size);
            req.setPage(1);
            pageable = new PageRequest(req.getPage() - 1, size);
            pageData = cookTaskDao.findAll(getRecipeCookTaskSpecification(req), pageable);
            List<RecipeCookTask> content = pageData.getContent();
            vos = convertDBResult2RecipeDetailVO(content);
            vos = vos.stream().sorted(Comparator.comparingLong(RecordRecipeDetailVO::getTimeConsumed).reversed()).collect(Collectors.toList());
            log.info("降序排序后{}", vos.stream().map(RecordRecipeDetailVO::getTimeConsumed).collect(Collectors.toList()));
            Page<RecordRecipeDetailVO> rets = ConvertList2PageVOUtils.convertList2PageVO(vos, srcpage, srcsize);
            return new PageImpl<>(rets.getContent(), pageable, pageData.getTotalElements());
        }

        return new PageImpl<>(vos, pageable, pageData.getTotalElements());
    }

    /**
     * Dto 装 VO 烹饪明细
     *
     * @param content
     * @return
     */
    public List<RecordRecipeDetailVO> convertDBResult2RecipeDetailVO(List<RecipeCookTask> content) {
        if (CollectionUtils.isEmpty(content)) {
            return new ArrayList<>(0);
        }
        List<String> recipeIds;
        recipeIds = content.stream().distinct().map(RecipeCookTask::getRecipeId).collect(Collectors.toList());
        Map<String, String> recipeId2RecipeCreatorMap = recipeServer.listByRecipeIds(recipeIds).getData().stream().distinct().collect(Collectors.toMap(StoreRecipeSimpleVo::getRecipeId, StoreRecipeSimpleVo::getPublishStoreName));
        RecipeCookTask task;
        List<RecordRecipeDetailVO> vos = new ArrayList<>();
        for (int i = 0; i < content.size(); i++) {
            task = content.get(i);
            RecordRecipeDetailVO detailVO = new RecordRecipeDetailVO();
            detailVO.setCookTime(task.getCookTime())
                    .setDeviceAlias(task.getDeviceAlias())
                    .setDeviceCgid(task.getDeviceCgid())
                    .setProductKey(task.getProductKey())
                    .setRecipeId(task.getRecipeId())
                    .setRecipeName(task.getRecipeName())
                    .setStoreId(task.getStoreId())
                    .setStatus(task.getStatus())
                    .setId(task.getId());
            if (storeId2StoreMainInfoMap.keySet().contains(task.getStoreId())) {
                detailVO.setStoreName(storeId2StoreMainInfoMap.get(task.getStoreId()).getStoreName())
                        .setMerchantId(storeId2StoreMainInfoMap.get(task.getStoreId()).getMerchantId())
                        .setMerchantName(storeId2StoreMainInfoMap.get(task.getStoreId()).getMerchantName());
            }
            if (recipeId2RecipeCreatorMap.keySet().contains(task.getRecipeId())) {
                detailVO.setRecipeCreator(recipeId2RecipeCreatorMap.get(task.getRecipeId()));
            }
            if (task.getFinishTime() == 0L && task.getFailTime() == 0L) {
                detailVO.setTimeConsumed(0L);
            }
            if (task.getFailTime() != 0L) {
                detailVO.setTimeConsumed(task.getFailTime() - task.getCookTime());
            }
            if (task.getFinishTime() != 0L) {
                detailVO.setTimeConsumed(task.getFinishTime() - task.getCookTime());
            }
            vos.add(detailVO);
        }

        return vos;
    }

    /**
     * 设备维度
     *
     * @param req
     * @return
     */
    public Page<RecordDeviceVO> queryDeviceRecord(QueryDeviceReq req) {
        String sortWord = req.getSortWord();
        String direction = req.getDirection();
        Sort sort = new Sort(Sort.Direction.DESC, TASK_ADD_TIME_DEVICE);
        Pageable page = new PageRequest(req.getPage() - 1, req.getSize(), sort);
        if (StringUtils.isNotBlank(req.getMerchantId()) || StringUtils.isNotBlank(req.getStoreId()) || StringUtils.isNotBlank(req.getStoreName())) {
            String storeId = getStoreId(req.getStoreId(), req.getMerchantId(), req.getStoreName());
            if (StringUtils.isEmpty(storeId)) {
                return new PageImpl<>(new ArrayList<>(), page, 0);
            }
            req.setStoreId(storeId);
        }
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<RecordDeviceResult> criteriaQuery = cb.createQuery(RecordDeviceResult.class);
        Root<RecipeCookTask> root = criteriaQuery.from(RecipeCookTask.class);
        criteriaQuery.multiselect(
                root.get("deviceCgid"),
                root.get("deviceAlias"),
                root.get("productKey"),

                root.get("storeId"),
                cb.count(root.get("status"))
        );
        List<Predicate> predicateList = new ArrayList<>();
        if (!StringUtils.isEmpty(req.getStoreId())) {
            predicateList.add(cb.equal(root.get("storeId").as(String.class), req.getStoreId()));
        }
        if (!StringUtils.isEmpty(req.getProductKey())) {
            predicateList.add(cb.equal(root.get("productKey").as(String.class), req.getProductKey()));
        }
        if (!StringUtils.isEmpty(req.getDeviceCgid())) {
            predicateList.add(cb.equal(root.get("deviceCgid").as(String.class), req.getDeviceCgid()));
        }
        if (req.getStartTime() != null && req.getEndTime() != null) {
            predicateList.add(cb.greaterThanOrEqualTo(root.get(TASK_ADD_TIME_DEVICE).as(Long.class), req.getStartTime()));
            predicateList.add(cb.lessThanOrEqualTo(root.get(TASK_ADD_TIME_DEVICE).as(Long.class), req.getEndTime()));
        }
        Predicate[] p = new Predicate[predicateList.size()];
        criteriaQuery.where(cb.and(predicateList.toArray(p)));
        criteriaQuery.groupBy(root.get("deviceCgid"), root.get("deviceAlias"), root.get("productKey"), root.get("storeId"));
        List<RecordDeviceResult> counts = entityManager.createQuery(criteriaQuery).getResultList();

        counts = recordDeviceResultSort(counts, direction, sortWord);
        log.info("设备维度{}", counts.stream().map(RecordDeviceResult::getCuisineCounts).collect(Collectors.toList()));

        TypedQuery<RecordDeviceResult> createQuery = entityManager.createQuery(criteriaQuery);
        createQuery.setFirstResult((req.getPage() - 1) * req.getSize());
        createQuery.setMaxResults(req.getSize());
        final List<RecordDeviceResult> list = counts;

        final List<List<RecordDeviceResult>> partition = Lists.partition(list, page.getPageSize());
        if (CollectionUtils.isEmpty(list)) {
            return new PageImpl<>(new ArrayList<>(0), page, 0);
        }
        List<RecordDeviceResult> pageContent = partition.get(page.getPageNumber());
        List<RecordDeviceVO> recordDeviceVOS = new ArrayList<>(pageContent.size());
        for (int i = 0; i < pageContent.size(); i++) {
            RecordDeviceVO recordDeviceVO = new RecordDeviceVO();
            RecordDeviceResult tmp = pageContent.get(i);
            recordDeviceVO.setCuisineCounts(tmp.getCuisineCounts())
                    .setDeviceAlias(tmp.getDeviceAlias())
                    .setDeviceCgid(tmp.getDeviceCgid())
                    .setManualCuisine(tmp.getManualCuisine())
                    .setProductKey(tmp.getProductKey())
                    .setRecipeCuisineCount(tmp.getRecipeCuisineCount());
            if (storeId2StoreMainInfoMap.keySet().contains(tmp.getStoreId())) {
                recordDeviceVO.setStoreName(storeId2StoreMainInfoMap.get(tmp.getStoreId()).getStoreName())
                        .setMerchantId(storeId2StoreMainInfoMap.get(tmp.getStoreId()).getMerchantId())
                        .setMerchantName(storeId2StoreMainInfoMap.get(tmp.getStoreId()).getMerchantName())
                        .setStoreId(tmp.getStoreId());
            }
            recordDeviceVOS.add(recordDeviceVO);
        }
        log.info("设备维度返回数据:{}", recordDeviceVOS);
        return new PageImpl<>(recordDeviceVOS, page, counts.size());
    }

    /**
     * 菜谱维度:按字段全局正序或降序
     *
     * @param counts
     * @param direction
     * @param sortWord
     * @return
     */
    private List<RecordDeviceResult> recordDeviceResultSort(List<RecordDeviceResult> counts, String direction, String sortWord) {
        if (StringUtils.equals(direction, SortDirectionConst.DESC)) {
            if (StringUtils.equals(sortWord, SortWord.CUISINE_COUNTS)) {
                counts = counts.stream().sorted(Comparator.comparing(RecordDeviceResult::getCuisineCounts).reversed()).collect(Collectors.toList());
            }
            if (StringUtils.equals(sortWord, SortWord.MANUAL_CUISINE)) {
                counts = counts.stream().sorted(Comparator.comparing(RecordDeviceResult::getManualCuisine).reversed()).collect(Collectors.toList());
            }
            if (StringUtils.equals(sortWord, SortWord.RECIPE_CUISINE)) {
                counts = counts.stream().sorted(Comparator.comparing(RecordDeviceResult::getRecipeCuisineCount).reversed()).collect(Collectors.toList());
            }
        }

        if (StringUtils.equals(direction, SortDirectionConst.ASC)) {
            if (StringUtils.equals(sortWord, SortWord.CUISINE_COUNTS)) {
                counts = counts.stream().sorted(Comparator.comparing(RecordDeviceResult::getCuisineCounts)).collect(Collectors.toList());
            }
            if (StringUtils.equals(sortWord, SortWord.MANUAL_CUISINE)) {
                counts = counts.stream().sorted(Comparator.comparing(RecordDeviceResult::getManualCuisine)).collect(Collectors.toList());
            }
            if (StringUtils.equals(sortWord, SortWord.RECIPE_CUISINE)) {
                counts = counts.stream().sorted(Comparator.comparing(RecordDeviceResult::getRecipeCuisineCount)).collect(Collectors.toList());
            }
        }

        return counts;
    }


    public Page<RecordIngredientDto> queryIngredientRecordPage(QueryIngredientReq req) {
        PageHelper.startPage(req.getPage(), req.getSize());
        List<RecordIngredientDto> list = recipeCookTaskEntityMapper.selectRecipeIngredientTaskDetail(req);
        return new PageImpl<>(list, new PageRequest(req.getPage() - 1, req.getSize()), ((com.github.pagehelper.Page) list).getTotal());
    }

    /**
     * 食材调料明细,以recipe_cook_task 为数据源进行统计
     *
     * @param req
     * @return
     */
    public Page<RecordIngredientVO> queryIngredientRecord(QueryIngredientReq req) {
        Integer page = req.getPage();
        Integer size = req.getSize();

        if (page < 1 || size < 1) {
            return new PageImpl<>(new ArrayList<>(0), new PageRequest(0, 1), 0);
        }
        Pageable pageable = new PageRequest(page - 1, size);
        if (StringUtils.isNotBlank(req.getMerchantId()) || StringUtils.isNotBlank(req.getStoreId()) || StringUtils.isNotBlank(req.getStoreName())) {
            if (StringUtils.isEmpty(getStoreId(req.getStoreId(), req.getMerchantId(), req.getStoreName()))) {
                return new PageImpl<>(new ArrayList<>(), pageable, 0);
            }
            req.setStoreId(getStoreId(req.getStoreId(), req.getMerchantId(), req.getStoreName()));
        }
        // 查询所有某店铺或所有店铺菜谱
        // 处理菜谱数据、统计食材调料
        Map<String, Long> recipeId2Count = statisticRecipeCookTask(req);
        if (recipeId2Count == null) {
            return new PageImpl<>(new ArrayList<>(), pageable, 0);
        }
        List<String> recipeIds = new ArrayList<>(recipeId2Count.keySet());
        Map<String, List<RecipeIngredientVo>> recipeIngredientsMap = recipeService.getIngredientsMapByRecipeIds(recipeIds);
        Map<String, IngredientDatavVo> ingredientDatavVoMap = new HashMap<>();
        for (String key : recipeId2Count.keySet()) {
            List<RecipeIngredientVo> vos = recipeIngredientsMap.get(key);
            if (vos == null) {
                continue;
            }
            for (RecipeIngredientVo vo : vos) {
                if (vo.getIngredientQuantity() <= 0.0D) {
                    continue;
                }
                convertUnit(vo);
                String ingredientUniqueKey = vo.getIngredientName() + "_" + vo.getIngredientUnit() + "_" + vo.getIngredientType();
                IngredientDatavVo datavVo = ingredientDatavVoMap.get(ingredientUniqueKey);
                if (datavVo == null) {
                    datavVo = new IngredientDatavVo();
                    datavVo.setIngredientName(vo.getIngredientName());
                    datavVo.setIngredientUnit(vo.getIngredientUnit());
                    datavVo.setIngredientQuantity(vo.getIngredientQuantity() * recipeId2Count.get(key));
                    datavVo.setIngredientType(vo.getIngredientType());
                    datavVo.setIngredientId(vo.getIngredientId());
                    ingredientDatavVoMap.put(ingredientUniqueKey, datavVo);
                } else {
                    datavVo.setIngredientQuantity(datavVo.getIngredientQuantity() + vo.getIngredientQuantity() * recipeId2Count.get(key));
                }
            }
        }

        List<IngredientDatavVo> ingredientDatavVos = new ArrayList<>(ingredientDatavVoMap.values());
        List<RecordIngredientVO> records = new ArrayList<>();
        String storeName = null;
        String merchantId = null;
        String merchantName = "";
        String storeId = req.getStoreId();
        if (StringUtils.isNotEmpty(storeId)) {
            storeId = req.getStoreId();
            if (storeId2StoreMainInfoMap.keySet().contains(storeId)) {
                storeName = storeId2StoreMainInfoMap.get(storeId).getStoreName();
                merchantId = storeId2StoreMainInfoMap.get(storeId).getMerchantId();
                merchantName = storeId2StoreMainInfoMap.get(storeId).getMerchantName();
            }

        }
        for (int i = 0; i < ingredientDatavVos.size(); i++) {
            RecordIngredientVO ingredientVO = new RecordIngredientVO();
            IngredientDatavVo vo = ingredientDatavVos.get(i);
            ingredientVO.setTotal(vo.getIngredientQuantity())
                    .setUnit(vo.getIngredientUnit())
                    .setIngredientType(vo.getIngredientType())
                    .setIngredientName(vo.getIngredientName())
                    .setIngredientId(vo.getIngredientId());
            if (StringUtils.isNotEmpty(req.getStoreId())) {
                ingredientVO.setStoreId(storeId)
                        .setStoreName(storeName)
                        .setMerchantName(merchantName)
                        .setMerchantId(merchantId);
            }
            records.add(ingredientVO);
        }
        List<RecordIngredientVO> list = records.stream().sorted(Comparator.comparingInt(RecordIngredientVO::getIngredientType)).collect(Collectors.toList());

        if (StringUtils.equals(SortDirectionConst.DESC, req.getDirection())) {
            list = list.stream().sorted(Comparator.comparingInt(RecordIngredientVO::getIngredientType).thenComparingDouble(RecordIngredientVO::getTotal).reversed()).collect(Collectors.toList());
        }

        if (StringUtils.equals(SortDirectionConst.ASC, req.getDirection())) {
            list = list.stream().sorted(Comparator.comparingInt(RecordIngredientVO::getIngredientType).thenComparingDouble(RecordIngredientVO::getTotal)).collect(Collectors.toList());
        }

        if (StringUtils.isEmpty(req.getIngredientName()) && req.getIngredientId() == null) {
            return ConvertList2PageVOUtils.convertList2PageVO(list, pageable);
        }
        /**
         * 根据食材Id、食材名条件筛选
         */
        if (req.getIngredientId() != null && StringUtils.isNotBlank(req.getIngredientName())) {
            list = list.stream().filter(e -> e.getIngredientName().equals(req.getIngredientName())).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(list)) {
                if (list.get(0).getIngredientId().equals(req.getIngredientId())) {
                    return ConvertList2PageVOUtils.convertList2PageVO(list, pageable);
                }
            } else {
                return ConvertList2PageVOUtils.convertList2PageVO(list, pageable);
            }

        }
        /**
         *  筛选对应的食材Id
         */
        if (req.getIngredientId() != null) {
            list = list.stream().filter(e -> e.getIngredientId().equals(req.getIngredientId())).collect(Collectors.toList());
            return ConvertList2PageVOUtils.convertList2PageVO(list, pageable);
        }
        /**
         *  筛选对应的食材名称
         */
        if (!StringUtils.isEmpty(req.getIngredientName())) {
            list = list.stream().filter(e -> e.getIngredientName().equals(req.getIngredientName())).collect(Collectors.toList());
            return ConvertList2PageVOUtils.convertList2PageVO(list, pageable);
        }
        return ConvertList2PageVOUtils.convertList2PageVO(list, pageable);
    }


    /**
     * 查询商业云店铺信息
     *
     * @param storeId
     * @param merchantId
     * @param storeName
     * @return
     */
    private String getStoreId(String storeId, String merchantId, String storeName) {
        // 以商家id、storeName初选数据
        if (StringUtils.isNotBlank(storeName) || StringUtils.isNotBlank(merchantId)) {
            List<SearchStoresRet> storeRet = searchStoresByParam(storeName, merchantId);
            if (storeRet.size() == 1) {// memberId 必定匹配(无需考虑)
                if (StringUtils.isNotBlank(storeName)) {//需考虑storeName空否
                    if (StringUtils.isBlank(storeId)) {
                        return storeRet.get(0).getStore_name().equals(storeName) ? storeRet.get(0).getStore_id() : null;
                    }
                    //需考虑storeId空否,有值需匹配
                    return storeRet.get(0).getStore_id().equals(storeId) ? storeRet.get(0).getStore_id() : null;
                } else {//storeName空,必定是memberId查回来的数据
                    if (StringUtils.isBlank(storeId)) {//storeId空直接返回
                        return storeRet.get(0).getStore_id();
                    }
                    //storeId非空,匹配数据的storeId
                    return storeRet.get(0).getStore_id().equals(storeId) ? storeRet.get(0).getStore_id() : null;
                }
            }
            // 以商家id、storeName初选数据为空,没有查到数据直接返回空,无需匹配storeId
            if (CollectionUtils.isEmpty(storeRet)) {
                return null;
            }
            // 确定 -> storeName查询,且存在查询字段是别的店铺名的子串现象
            if (storeRet.size() > 1) {
                // 应用侧是店铺名精确查找、筛选数据
                List<SearchStoresRet> dtoList = storeRet.stream().filter(a -> a.getStore_name().equals(storeName)).collect(Collectors.toList());
                if (!CollectionUtils.isEmpty(dtoList)) {//精确匹配到数据
                    if (StringUtils.isBlank(storeId)) {// 需根据storeId匹配数据
                        return dtoList.get(0).getStore_id();
                    }
                    return dtoList.get(0).getStore_id().equals(storeId) ? storeId : null;
                } else {
                    return null;
                }
            }
        }
        // memberId、storeName均为空时,可以根据输入的storeId为依据返回,用于应用侧搜索数据
        log.info("根据商家账号{}或者店铺名{}或店铺Id{}查询", merchantId, storeName, storeId);
        return storeId;
    }

    @Scheduled(cron = "0 0/30 * * * ? ")
    @PostConstruct
    public void searchStores() {
        String storeName = null;
        String memberId = null;
        SearchStoresReq searchStoresReqDto = new SearchStoresReq();
        searchStoresReqDto.setMember_id(StringUtils.isNotBlank(memberId) ? memberId : "");
        searchStoresReqDto.setApp_id(businessCloudConfig.getAppId());
        searchStoresReqDto.setStore_name(StringUtils.isNotBlank(storeName) ? storeName : "");
        searchStoresReqDto.setOrder("store_addtime");
        searchStoresReqDto.setSort(SortDirectionConst.DESC);
        searchStoresReqDto.setPageno(1);
        searchStoresReqDto.setPagesize(Integer.MAX_VALUE);
        List<SearchStoresRet> storesDtos = new ArrayList<>();
        try {
            BasePageRet<List<SearchStoresRet>> searchStoresRet = shopServer.searchStores(searchStoresReqDto, tokenService.getBizCloudAdminTokenBearer());
            if (searchStoresRet != null && searchStoresRet.getCode() == 0 && searchStoresRet.getData() != null) {
                storesDtos = searchStoresRet.getData();
            }
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        Map<String, MerchantInfo2StoreInfoDto> merchant2StoreMap = new ConcurrentHashMap<>();
        for (SearchStoresRet s : storesDtos) {
            MerchantInfo2StoreInfoDto tmp = new MerchantInfo2StoreInfoDto();
            tmp.setMerchantId(s.getMember_id())
                    .setMerchantName(s.getMember_name())
                    .setStoreId(s.getStore_id())
                    .setStoreName(s.getStore_name());
            merchant2StoreMap.put(s.getStore_id(), tmp);
        }

        log.info("Schedule Task done!,查询商业云上店铺数量: {}   ", merchant2StoreMap.size());
        List<String> stores = storesDtos.stream().map(SearchStoresRet::getStore_name).collect(Collectors.toList());
        storeNames.clear();
        storeId2StoreMainInfoMap.clear();
        setStoreNames(stores);
        setStoreId2StoreMainInfoMap(merchant2StoreMap);
    }

    /**
     * 通过店铺名、商家账户搜素店铺
     *
     * @param storeName
     * @param memberId
     * @return
     */
    public List<SearchStoresRet> searchStoresByParam(String storeName, String memberId) {
        SearchStoresReq searchStoresReqDto = new SearchStoresReq();
        searchStoresReqDto.setApp_id(businessCloudConfig.getAppId());
        searchStoresReqDto.setMember_id(StringUtils.isNotBlank(memberId) ? memberId : "");
        searchStoresReqDto.setStore_name(StringUtils.isNotBlank(storeName) ? storeName : "");
        searchStoresReqDto.setDistance(null);
        searchStoresReqDto.setOrder("store_addtime");
        searchStoresReqDto.setSort("DESC");
        searchStoresReqDto.setPageno(1);
        searchStoresReqDto.setPagesize(Integer.MAX_VALUE);
        BasePageRet<List<SearchStoresRet>> searchStoresRet = shopServer.searchStores(searchStoresReqDto, tokenService.getBizCloudAdminTokenBearer());
        return searchStoresRet.getData();
    }


    /**
     * 转换食材单位
     *
     * @param vo
     */
    private void convertUnit(RecipeIngredientVo vo) {
        String unit = vo.getIngredientUnit();
        if (StringUtils.equalsIgnoreCase(unit, UNIT_CONST.GRAM)) {
            vo.setIngredientUnit("克");
        } else if (StringUtils.equals(unit, UNIT_CONST.KG) || StringUtils.equalsIgnoreCase(unit, UNIT_CONST.KiloGram)) {
            vo.setIngredientUnit("克");
            vo.setIngredientQuantity(vo.getIngredientQuantity() * 1000);
        } else if (StringUtils.equalsIgnoreCase(unit, UNIT_CONST.MILLILITER)) {
            vo.setIngredientUnit("毫升");
        } else if (StringUtils.equals(unit, UNIT_CONST.LITRE_C) || StringUtils.equalsIgnoreCase(unit, UNIT_CONST.LITRE)) {
            vo.setIngredientUnit("毫升");
            vo.setIngredientQuantity(vo.getIngredientQuantity() * 1000);
        }
    }

    public interface UNIT_CONST {
        String GRAM = "g";
        String KiloGram = "kg";
        String KG = "千克";
        String LITRE_C = "升";
        String LITRE = "l";
        String MILLILITER = "ml";
    }

    /**
     * 创建EXCEL表格
     *
     * @return
     */
    private HSSFWorkbook createWorkbook(String sheetName, String[] sheetHeaders) {

        HSSFWorkbook wb = new HSSFWorkbook();
        //创建工作薄
        HSSFSheet sheet = wb.createSheet(sheetName);
        if (sheetName.contains(ReportDownloadRecordEntity.DIMENSION.MERCHANT.getDimensionName())) {
            sheet.setColumnWidth(0, 5000);
            sheet.setColumnWidth(1, 6000);
            sheet.setColumnWidth(2, 6000);
        }

        if (sheetName.contains(ReportDownloadRecordEntity.DIMENSION.RECIPE.getDimensionName())) {
            sheet.setColumnWidth(0, 8000);
            sheet.setColumnWidth(1, 6000);
            sheet.setColumnWidth(2, 6000);
            sheet.setColumnWidth(3, 6000);
            sheet.setColumnWidth(4, 6000);
            sheet.setColumnWidth(6, 5000);
            sheet.setColumnWidth(7, 5000);
            sheet.setColumnWidth(8, 5000);
        }

        if (sheetName.contains(ReportDownloadRecordEntity.DIMENSION.RECIPE_DETAIL.getDimensionName())) {

            sheet.setColumnWidth(0, 6000);
            sheet.setColumnWidth(1, 6000);
            sheet.setColumnWidth(2, 4000);
            sheet.setColumnWidth(3, 5000);
            sheet.setColumnWidth(4, 6000);
            sheet.setColumnWidth(5, 6000);
            sheet.setColumnWidth(8, 6000);
            sheet.setColumnWidth(9, 11000);
            sheet.setColumnWidth(10, 7000);

        }

        if (sheetName.contains(ReportDownloadRecordEntity.DIMENSION.INGREDIENT.getDimensionName())) {
            sheet.setColumnWidth(2, 5000);
            sheet.setColumnWidth(3, 5000);
            sheet.setColumnWidth(4, 6000);
        }

        if (sheetName.contains(ReportDownloadRecordEntity.DIMENSION.DEVICE.getDimensionName())) {

            sheet.setColumnWidth(0, 11000);
            sheet.setColumnWidth(1, 7000);
            sheet.setColumnWidth(2, 7000);
            sheet.setColumnWidth(3, 5000);
            sheet.setColumnWidth(4, 6000);
            sheet.setColumnWidth(5, 6000);
        }

        // 表头样式
        CellStyle headerStyle = wb.createCellStyle();
        headerStyle.setWrapText(true);
        //水平居中
        headerStyle.setAlignment(CellStyle.ALIGN_CENTER);
        //垂直居中
        headerStyle.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
        //设置边框
        headerStyle.setBorderTop(CellStyle.BORDER_THIN);
        headerStyle.setBorderRight(CellStyle.BORDER_THIN);
        headerStyle.setBorderBottom(CellStyle.BORDER_THIN);
        headerStyle.setBorderLeft(CellStyle.BORDER_THIN);
        //设置颜色
        headerStyle.setFillForegroundColor(HSSFColor.LIGHT_YELLOW.index);
        headerStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);

        Font headerFont = wb.createFont();
        headerFont.setFontHeightInPoints((short) 12);
        headerFont.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
        headerStyle.setFont(headerFont);

        // 单元格样式
        CellStyle cellStyle = wb.createCellStyle();
        cellStyle.setAlignment(CellStyle.ALIGN_CENTER);
        //垂直居中
        cellStyle.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
        //设置边框
        cellStyle.setBorderTop(CellStyle.BORDER_THIN);
        cellStyle.setBorderBottom(CellStyle.BORDER_THIN);
        cellStyle.setBorderLeft(CellStyle.BORDER_THIN);
        cellStyle.setBorderRight(CellStyle.BORDER_THIN);
        HSSFRow row = sheet.createRow(0);
        //冻结首行
        sheet.createFreezePane(0, 1, 0, 1);
        HSSFCell cell;

        cellStyle.setFillForegroundColor(HSSFColor.BLACK.index);
        cellStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);

        //设置字体
        Font cellFont = wb.createFont();
        cellFont.setBoldweight(HSSFFont.BOLDWEIGHT_NORMAL);
        cellStyle.setFont(cellFont);

        //自动换行
        cellStyle.setWrapText(true);
        for (int i = 0; i < sheetHeaders.length; i++) {
            cell = row.createCell(i);
            cell.setCellValue(sheetHeaders[i]);
            row.getCell(i).setCellStyle(headerStyle);
        }
        return wb;
    }


    /**
     * 将excel对象转换为字节流数组
     *
     * @param xwb
     * @return
     * @throws IOException
     */
    public byte[] XWorkbook2ByteArray(XSSFWorkbook xwb) throws IOException {
        if (null == xwb) {
            return null;
        }
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        xwb.write(os);
        return os.toByteArray();
    }


    /**
     * 获取String类型时间
     *
     * @return
     */
    public String convertCalender2String() {
        SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
        Calendar calendar = Calendar.getInstance();
        return df.format(calendar.getTime());
    }


    /**
     * Excel 表头
     */
    public interface SHEET_HEADERS {
        String[] MERCHANT = {"商家账号", "商家名称", "商家ID", "店铺名称", "店铺ID", "烹饪菜谱个数", "烹饪次数", "烹饪异常次数", "使用设备的天数"};
        String[] RECIPE = {"菜谱名称", "菜谱ID", "创作方", "商家名称", "商家ID", "店铺名称", "店铺ID", "商家数", "烹饪次数", "异常烹饪次数", "设备类型"};
        String[] RECIPE_DETAIL = {"菜谱名称", "菜谱ID", "商家账号", "商家名称", "商家ID", "店铺名称", "店铺ID", "烹饪开始时间", "任务耗时", "任务状态", "设备类型", "设备CGID", "设备别名"};
        String[] INGREDIENT = {"用料名称", "用料ID", "商家账号", "商家名称", "商家ID", "店铺名称", "店铺ID", "单位", "总量"};
        String[] DEVICE = {"设备CGID", "设备别名", "设备类型", "商家账号", "商家名称", "商家ID", "店铺名称", "店铺ID", "总烹饪次数", "菜谱烹饪", "手动烹饪"};
    }


    /**
     * 烹饪记录: 根据条件查询商家维度
     *
     * @param req
     * @return
     */
    public List<RecordMerchantVO> listMerchantRecords(QueryRecordReq req) {
        if (StringUtils.isNotBlank(req.getMerchantId()) || StringUtils.isNotBlank(req.getStoreName()) || StringUtils.isNotBlank(req.getStoreId())) {
            String storeId = getStoreId(req.getStoreId(), req.getMerchantId(), req.getStoreName());
            if (StringUtils.isEmpty(storeId)) {
                return new ArrayList<>();
            }
            req.setStoreId(storeId);
        }
        String sortWord = req.getSortWord();
        String direction = req.getDirection();
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<RecordMerchantResult> criteriaQuery = cb.createQuery(RecordMerchantResult.class);
        Root<RecipeCookTask> root = criteriaQuery.from(RecipeCookTask.class);
        criteriaQuery.multiselect(root.get("storeId"),
                cb.count(cb.selectCase().when(root.get("status").in(Arrays.asList(RecipeCookTask.STATUS.FAILURE, RecipeCookTask.STATUS.CANCEL)), 1).otherwise(cb.nullLiteral(Number.class))),
                cb.count(cb.selectCase().when(root.get("status").in(Arrays.asList(RecipeCookTask.STATUS.FINISHED)), 1).otherwise(cb.nullLiteral(Number.class))),
                cb.count(root.get("storeId")),
                cb.countDistinct((root.get("taskDate"))),
                cb.countDistinct(root.get("recipeId"))
        );

        List<Predicate> predicateList = new ArrayList<>();
        if (!StringUtils.isEmpty(req.getStoreId())) {
            predicateList.add(cb.equal(root.get("storeId").as(String.class), req.getStoreId()));
        }
        // if (!StringUtils.isEmpty(req.getMerchantId())) {
        //     predicateList.add(cb.like(root.get("merchantId").as(String.class), "%" + req.getMerchantId().trim() + "%"));
        // }
        if (req.getStartTime() != null && req.getEndTime() != null) {
            predicateList.add(cb.greaterThanOrEqualTo(root.get(TASK_ADD_TIME_DEVICE).as(Long.class), req.getStartTime()));
            predicateList.add(cb.lessThanOrEqualTo(root.get(TASK_ADD_TIME_DEVICE).as(Long.class), req.getEndTime()));
        }
        Predicate[] p = new Predicate[predicateList.size()];
        // 条件筛选
        criteriaQuery.where(cb.and(predicateList.toArray(p)));
        // 分组
        criteriaQuery.groupBy(root.get("storeId"));

        TypedQuery<RecordMerchantResult> createQuery = entityManager.createQuery(criteriaQuery);
        createQuery.setFirstResult(0);
        createQuery.setMaxResults(Integer.MAX_VALUE);
        List<RecordMerchantResult> list = entityManager.createQuery(criteriaQuery).getResultList();
        log.info("查询商家维度数据大小  {}", list.size());
        if (CollectionUtils.isEmpty(list)) {
            return new ArrayList<>();
        }
        list = sortByProperty(list, direction, sortWord);

        //返回查询的分页结果,createQuery.getResultList()为分页查询的结果对象,counts.size()为设置分页参数之前查询的总数
        List<RecordMerchantVO> vos = convertResult2VO(list);

        return vos;
    }

    /**
     * 烹饪记录: 根据条件查询菜谱维度所有数据
     *
     * @param req
     * @return
     */
    public List<RecordRecipeVO> listRecipeRecords(QueryRecipeReq req) {
        String sortWord = req.getSortWord();
        String direction = req.getDirection();
        if (StringUtils.isNotBlank(req.getStoreName()) || StringUtils.isNotBlank(req.getStoreId()) || StringUtils.isNotBlank(req.getMerchantId())) {
            String storeId = getStoreId(req.getStoreId(), req.getMerchantId(), req.getStoreName());
            if (StringUtils.isEmpty(storeId)) {
                return new ArrayList<>();
            }
            req.setStoreId(storeId);
        }

        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<RecordRecipeResult> criteriaQuery = cb.createQuery(RecordRecipeResult.class);
        Root<RecipeCookTask> root = criteriaQuery.from(RecipeCookTask.class);
        criteriaQuery.multiselect(
                root.get("recipeName"),
                root.get("recipeId"),
                root.get("productKey"),
                cb.count(cb.selectCase().when(root.get("status").in(Arrays.asList(RecipeCookTask.STATUS.FAILURE, RecipeCookTask.STATUS.CANCEL)), 1).otherwise(cb.nullLiteral(Number.class))),
                cb.count(root.get("status")),
                cb.countDistinct(root.get("storeId")));
        List<Predicate> predicateList = new ArrayList<>();
        if (!StringUtils.isEmpty(req.getRecipeId())) {
            predicateList.add(cb.equal(root.get("recipeId").as(String.class), req.getRecipeId()));
        }
        if (!StringUtils.isEmpty(req.getRecipeName())) {
            predicateList.add(cb.like(root.get("recipeName").as(String.class), "%" + req.getRecipeName().trim() + "%"));
        }
        if (!StringUtils.isEmpty(req.getStoreId())) {
            predicateList.add(cb.equal(root.get("storeId").as(String.class), req.getStoreId()));
        }
        // if (!StringUtils.isEmpty(req.getMerchantId())) {
        //     predicateList.add(cb.like(root.get("merchantId").as(String.class), "%" + req.getMerchantId().trim() + "%"));
        // }
        if (req.getStartTime() != null && req.getEndTime() != null) {
            predicateList.add(cb.greaterThanOrEqualTo(root.get(TASK_ADD_TIME_DEVICE).as(Long.class), req.getStartTime()));
            predicateList.add(cb.lessThanOrEqualTo(root.get(TASK_ADD_TIME_DEVICE).as(Long.class), req.getEndTime()));
        }
        Predicate[] p = new Predicate[predicateList.size()];
        criteriaQuery.where(cb.and(predicateList.toArray(p)));
        criteriaQuery.groupBy(root.get("recipeId"), root.get("recipeName"), root.get("productKey"));

        // TODO 查询所有菜谱的创作方
        List<RecordRecipeResult> counts = entityManager.createQuery(criteriaQuery).getResultList();
        TypedQuery<RecordRecipeResult> createQuery = entityManager.createQuery(criteriaQuery);
        createQuery.setFirstResult(0);
        createQuery.setMaxResults(Integer.MAX_VALUE);
        counts = recordRecipeResultsSort(counts, direction, sortWord);
        Map<String, String> recipeId2RecipeCreatorMap = new HashMap<>();
        List<String> recipeIds;
        recipeIds = counts.stream().distinct().map(RecordRecipeResult::getRecipeId).collect(Collectors.toList());
        if (recipeIds.size() <= 10) {
            recipeId2RecipeCreatorMap = recipeServer.listByRecipeIds(recipeIds).getData().stream().collect(Collectors.toMap(StoreRecipeSimpleVo::getRecipeId, StoreRecipeSimpleVo::getPublishStoreName));
        }
        final List<String> list = recipeIds;
        final List<List<String>> parts = Lists.partition(list, 10);
        for (int i = 0; i < parts.size(); i++) {
            // TODO MultiThread
            log.info("菜谱维度调试:{}", parts.get(i));
            recipeId2RecipeCreatorMap.putAll(recipeServer.listByRecipeIds(parts.get(i)).getData().stream().collect(Collectors.toMap(StoreRecipeSimpleVo::getRecipeId, StoreRecipeSimpleVo::getPublishStoreName)));
        }
        String storeId = req.getStoreId();
        String storeName = null;
        String merchantId = null;
        String merchantName = "";
        if (StringUtils.isNotEmpty(storeId) && storeId2StoreMainInfoMap.keySet().contains(storeId)) {
            storeName = storeId2StoreMainInfoMap.get(storeId).getStoreName();
            merchantId = storeId2StoreMainInfoMap.get(storeId).getMerchantId();
            merchantName = storeId2StoreMainInfoMap.get(storeId).getMerchantName();

        }
        List<RecordRecipeVO> vos = new ArrayList<>();
        for (int i = 0; i < counts.size(); i++) {
            RecordRecipeResult tmp = counts.get(i);
            RecordRecipeVO vo = new RecordRecipeVO();
            if (StringUtils.isNotEmpty(storeId)) {
                vo.setMerchantId(merchantId)
                        .setStoreId(storeId)
                        .setStoreName(storeName)
                        .setMerchantName(merchantName);

            }
            vo.setStoreCount(tmp.getStoreCount())
                    .setRecipeId(tmp.getRecipeId())
                    .setCuisineCount(tmp.getCuisineCount())
                    .setProductKey(tmp.getProductKey())
                    .setErrorCuisineCount(tmp.getErrorCuisineCount())
                    .setRecipeName(tmp.getRecipeName());
            if (recipeId2RecipeCreatorMap.keySet().contains(tmp.getRecipeId())) {
                vo.setRecipeCreator(recipeId2RecipeCreatorMap.get(tmp.getRecipeId()));
            }
            vos.add(vo);
        }
        return vos;
    }

    /**
     * 烹饪记录: 根据条件查询菜谱烹饪明细所有数据
     *
     * @param req
     * @return
     */
    public List<RecordRecipeDetailVO> listRecipeDetailRecords(QueryRecipeReq req) {
        req.setPage(1);
        req.setSize(Integer.MAX_VALUE);
        log.info("导出明细请求参数:{}", req);
        Page<RecordRecipeDetailVO> pageData = queryRecipeDetailRecord(req);
        List<RecordRecipeDetailVO> list = pageData.getContent();

        return list;
    }

    /**
     * 烹饪记录: 根据条件查询设备维度所有数据
     *
     * @param req
     * @return
     */
    public List<RecordDeviceVO> listDeviceVOs(QueryDeviceReq req) {
        String sortWord = req.getSortWord();
        String direction = req.getDirection();
        String storeId;
        if (StringUtils.isNotBlank(req.getStoreName()) || StringUtils.isNotBlank(req.getStoreId()) || StringUtils.isNotBlank(req.getMerchantId())) {
            storeId = getStoreId(req.getStoreId(), req.getMerchantId(), req.getStoreName());
            if (StringUtils.isEmpty(storeId)) {
                return new ArrayList<>();
            }
            req.setStoreId(storeId);
        }
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<RecordDeviceResult> criteriaQuery = cb.createQuery(RecordDeviceResult.class);
        Root<RecipeCookTask> root = criteriaQuery.from(RecipeCookTask.class);
        criteriaQuery.multiselect(
                root.get("deviceCgid"),
                root.get("deviceAlias"),
                root.get("productKey"),

                root.get("storeId"),
                cb.count(root.get("status"))
        );
        List<Predicate> predicateList = new ArrayList<>();
        if (!StringUtils.isEmpty(req.getStoreId())) {
            predicateList.add(cb.equal(root.get("storeId").as(String.class), req.getStoreId()));
        }
        if (!StringUtils.isEmpty(req.getProductKey())) {
            predicateList.add(cb.equal(root.get("productKey").as(String.class), req.getProductKey()));
        }
        if (!StringUtils.isEmpty(req.getDeviceCgid())) {
            predicateList.add(cb.equal(root.get("deviceCgid").as(String.class), req.getDeviceCgid()));
        }
        if (req.getStartTime() != null && req.getEndTime() != null) {
            predicateList.add(cb.greaterThanOrEqualTo(root.get("addTime").as(Long.class), req.getStartTime()));
            predicateList.add(cb.lessThanOrEqualTo(root.get("addTime").as(Long.class), req.getEndTime()));
        }
        Predicate[] p = new Predicate[predicateList.size()];
        criteriaQuery.where(cb.and(predicateList.toArray(p)));
        criteriaQuery.groupBy(root.get("deviceCgid"), root.get("deviceAlias"), root.get("productKey"), root.get("storeId"));
        List<RecordDeviceResult> counts = entityManager.createQuery(criteriaQuery).getResultList();

        if ("DESC".equals(direction)) {

            if (StringUtils.equals(sortWord, "recipeCuisineCount")) {
                counts = counts.stream().sorted(Comparator.comparing(RecordDeviceResult::getRecipeCuisineCount).reversed()).collect(Collectors.toList());
            }

            if (StringUtils.equals(sortWord, "cuisineCounts")) {
                counts = counts.stream().sorted(Comparator.comparing(RecordDeviceResult::getCuisineCounts).reversed()).collect(Collectors.toList());
            }
            if (StringUtils.equals(sortWord, "manualCuisine")) {
                counts = counts.stream().sorted(Comparator.comparing(RecordDeviceResult::getManualCuisine).reversed()).collect(Collectors.toList());
            }

        }

        if ("ASC".equals(direction)) {
            if (StringUtils.equals(sortWord, "cuisineCounts")) {
                counts = counts.stream().sorted(Comparator.comparing(RecordDeviceResult::getCuisineCounts)).collect(Collectors.toList());
            }

            if (StringUtils.equals(sortWord, "recipeCuisineCount")) {
                counts = counts.stream().sorted(Comparator.comparing(RecordDeviceResult::getRecipeCuisineCount)).collect(Collectors.toList());
            }

            if (StringUtils.equals(sortWord, "manualCuisine")) {
                counts = counts.stream().sorted(Comparator.comparing(RecordDeviceResult::getManualCuisine)).collect(Collectors.toList());
            }
        }
        List<RecordDeviceVO> recordDeviceVOS = new ArrayList<>(counts.size());
        for (int i = 0; i < counts.size(); i++) {
            RecordDeviceVO recordDeviceVO = new RecordDeviceVO();
            RecordDeviceResult tmp = counts.get(i);
            recordDeviceVO.setCuisineCounts(tmp.getCuisineCounts())
                    .setProductKey(tmp.getProductKey())
                    .setDeviceAlias(tmp.getDeviceAlias())
                    .setDeviceCgid(tmp.getDeviceCgid())
                    .setManualCuisine(tmp.getManualCuisine())
                    .setRecipeCuisineCount(tmp.getRecipeCuisineCount())
                    .setStoreId(tmp.getStoreId());
            if (storeId2StoreMainInfoMap.keySet().contains(tmp.getStoreId())) {
                recordDeviceVO.setStoreName(storeId2StoreMainInfoMap.get(tmp.getStoreId()).getStoreName())
                        .setMerchantId(storeId2StoreMainInfoMap.get(tmp.getStoreId()).getMerchantId())
                        .setMerchantName(storeId2StoreMainInfoMap.get(tmp.getStoreId()).getMerchantName());
            }
            recordDeviceVOS.add(recordDeviceVO);
        }
        return recordDeviceVOS;
    }

    /**
     * 导出商家维度记录
     *
     * @param merchantVOS
     * @return
     */
    public HSSFWorkbook exportMerchantData(List<RecordMerchantVO> merchantVOS) {
        String sheetName = ReportDownloadRecordEntity.DIMENSION.MERCHANT.getDimensionName().concat(convertCalender2String());
        HSSFWorkbook wb = createWorkbook(sheetName, SHEET_HEADERS.MERCHANT);
        if (wb == null) {
            throw new BaseRetException(BaseRet.createFailureRet("创建EXCEL失败!"));
        }
        int rowNum = 1;
        HSSFSheet sheet = wb.getSheet(sheetName);
        if (sheet == null) {
            throw new BaseRetException(BaseRet.createFailureRet("获取EXCEL工作簿失败!"));
        }
        if (CollectionUtils.isEmpty(merchantVOS)) {
            return wb;
        }
        HSSFCellStyle cellStyle = wb.createCellStyle();
        for (RecordMerchantVO vo : merchantVOS) {
            if (null == vo) {
                continue;
            }
            Integer cellNum = 0;
            HSSFRow row = sheet.createRow(rowNum++);
            // "商家账号"
            HSSFCell cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(vo.getMerchantId());

            //"店铺名称"
            cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(vo.getStoreName());

            // "店铺ID"
            cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(vo.getStoreId());

            // 菜谱烹饪个数
            // 烹饪菜谱个数(统计商家烹饪的菜谱个数,如果商家有**100**个,使用了其中**80**个烹饪总次数2000,展示为**80**)
            //  烹饪菜谱次数(统计商家烹饪的菜谱个数,如果商家有**100**个,使用了其中80个烹饪总次数**2000**,展示为**2000**)
            cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(vo.getRecipeUsedCount());

            //"烹饪次数"
            cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(vo.getCuisineCount());

            // "烹饪异常次数"
            cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(vo.getErrorCuisineCount());

            // 设备使用天数
            cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(vo.getDeviceUsageDays());
        }
        return wb;
    }

    /**
     * 导出菜谱数据
     *
     * @param recipeResults
     * @return
     */
    public HSSFWorkbook exportRecipeData(List<RecordRecipeVO> recipeResults) {
        String sheetName = ReportDownloadRecordEntity.DIMENSION.RECIPE.getDimensionName().concat(convertCalender2String());
        HSSFWorkbook wb = createWorkbook(sheetName, SHEET_HEADERS.RECIPE);
        if (wb == null) {
            throw new BaseRetException(BaseRet.createFailureRet("创建EXCEL失败!"));
        }
        int rowNum = 1;
        HSSFSheet sheet = wb.getSheet(sheetName);
        if (sheet == null) {
            throw new BaseRetException(BaseRet.createFailureRet("获取EXCEL工作簿失败!"));
        }
        if (CollectionUtils.isEmpty(recipeResults)) {
            return wb;
        }
        HSSFCellStyle cellStyle = wb.createCellStyle();
        for (RecordRecipeVO vo : recipeResults) {
            if (null == vo) {
                continue;
            }
            Integer cellNum = 0;
            HSSFRow row = sheet.createRow(rowNum++);
            // "菜谱名称"
            HSSFCell cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(vo.getRecipeName());

            //"菜谱ID"
            cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(vo.getRecipeId());

            // 店铺名称
            cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(vo.getStoreName());

            // 店铺ID
            cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(vo.getStoreId());

            // // 商家账号
            // cell = row.createCell(cellNum++);
            // cell.setCellStyle(cellStyle);
            // cell.setCellValue(vo.getMerchantId());

            // "创作方"
            cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(vo.getRecipeCreator());

            // "商家数"
            cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(vo.getStoreCount());

            //"烹饪次数"
            cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(vo.getCuisineCount());

            // "烹饪异常次数"
            cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(vo.getErrorCuisineCount());

            // 设备类型
            cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(iotProductKey.getProductKey2Name().get(vo.getProductKey()));
        }
        return wb;
    }

    /**
     * 菜谱明细
     *
     * @param recipeDetailVOS
     * @return
     */
    public HSSFWorkbook exportRecipeDetailData(List<RecordRecipeDetailVO> recipeDetailVOS) {
        String sheetName = ReportDownloadRecordEntity.DIMENSION.RECIPE_DETAIL.getDimensionName().concat(convertCalender2String());
        HSSFWorkbook wb = createWorkbook(sheetName, SHEET_HEADERS.RECIPE_DETAIL);
        if (wb == null) {
            throw new BaseRetException(BaseRet.createFailureRet("创建EXCEL失败!"));
        }
        int rowNum = 1;
        HSSFSheet sheet = wb.getSheet(sheetName);
        if (sheet == null) {
            throw new BaseRetException(BaseRet.createFailureRet("获取EXCEL工作簿失败!"));
        }
        if (CollectionUtils.isEmpty(recipeDetailVOS)) {
            return wb;
        }
        HSSFCellStyle cellStyle = wb.createCellStyle();
        SimpleDateFormat df = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        for (RecordRecipeDetailVO vo : recipeDetailVOS) {
            if (null == vo) {
                continue;
            }
            Integer cellNum = 0;
            HSSFRow row = sheet.createRow(rowNum++);
            // "菜谱名称"
            HSSFCell cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(vo.getRecipeName());

            //"菜谱ID"
            cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(vo.getRecipeId());

            // "商家账号"
            cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(vo.getMerchantId());

            //"店铺名称"
            cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(vo.getStoreName());

            //"店铺ID"
            cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(vo.getStoreId());

            // "烹饪开始时间"
            cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            long cookTime = vo.getCookTime();
            if (cookTime == 0L) {
                cell.setCellValue("--");
            } else {
                cell.setCellValue(df.format(cookTime));
            }


            // "烹饪耗时"
            cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            long timeConsumed = (vo.getTimeConsumed());
            if (timeConsumed == 0L) {
                cell.setCellValue("--");
            } else {
                cell.setCellValue(TimeUtils.getTimeString(timeConsumed));
            }


            // 任务状态
            cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            String status;
            switch (vo.getStatus()) {
                case 1:
                    status = "烹饪中";
                    break;
                case 2:
                    status = "烹饪完成";
                    break;
                case 3:
                    status = "烹饪失败";
                    break;
                case 4:
                    status = "取消";
                    break;
                default:
                    status = "未开始";
            }
            cell.setCellValue(status);

            // 设备类型
            cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(iotProductKey.getProductKey2Name().get(vo.getProductKey()));

            // 设备CGID
            cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(vo.getDeviceCgid());

            // 设备别名
            cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(vo.getDeviceAlias());
        }
        return wb;
    }

    /**
     * 食材调料
     *
     * @param vos
     * @return
     */
    public HSSFWorkbook exportIngredientData(List<RecordIngredientVO> vos) {
        String sheetName = ReportDownloadRecordEntity.DIMENSION.INGREDIENT.getDimensionName().concat(convertCalender2String());
        HSSFWorkbook wb = createWorkbook(sheetName, SHEET_HEADERS.INGREDIENT);
        if (wb == null) {
            throw new BaseRetException(BaseRet.createFailureRet("创建EXCEL失败!"));
        }
        int rowNum = 1;
        HSSFSheet sheet = wb.getSheet(sheetName);
        if (sheet == null) {
            throw new BaseRetException(BaseRet.createFailureRet("获取EXCEL工作簿失败!"));
        }
        if (CollectionUtils.isEmpty(vos)) {
            return wb;
        }
        HSSFCellStyle cellStyle = wb.createCellStyle();
        log.info("食材名称{}", vos.stream().map(RecordIngredientVO::getIngredientName).collect(Collectors.toList()));
        for (RecordIngredientVO vo : vos) {
            // if (null == vo) {
            //     continue;
            // }
            log.info("食材名称:{}", vo.getIngredientName());
            Integer cellNum = 0;
            HSSFRow row = sheet.createRow(rowNum++);
            // "食材名称"
            HSSFCell cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(vo.getIngredientName());

            //"食材ID"
            cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(vo.getIngredientId());

            // "商家账号"

            cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(vo.getMerchantId());

            //"店铺名称
            cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(vo.getStoreName());

            // 店铺ID
            cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(vo.getStoreId());

            // 单位
            cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(vo.getUnit());

            //"总量"
            cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(vo.getTotal());
        }
        return wb;
    }


    /**
     * 设备烹饪数据
     *
     * @param vos
     * @return
     */
    public HSSFWorkbook exportDeviceData(List<RecordDeviceVO> vos) {
        String sheetName = ReportDownloadRecordEntity.DIMENSION.DEVICE.getDimensionName().concat(convertCalender2String());
        HSSFWorkbook wb = createWorkbook(sheetName, SHEET_HEADERS.DEVICE);
        if (wb == null) {
            throw new BaseRetException(BaseRet.createFailureRet("创建EXCEL失败!"));
        }
        int rowNum = 1;
        HSSFSheet sheet = wb.getSheet(sheetName);
        if (sheet == null) {
            throw new BaseRetException(BaseRet.createFailureRet("获取EXCEL工作簿失败!"));
        }
        if (CollectionUtils.isEmpty(vos)) {
            return wb;
        }
        HSSFCellStyle cellStyle = wb.createCellStyle();
        for (RecordDeviceVO vo : vos) {
            if (null == vo) {
                continue;
            }
            Integer cellNum = 0;
            HSSFRow row = sheet.createRow(rowNum++);
            // 设备CGID
            HSSFCell cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(vo.getDeviceCgid());

            // 设备别名
            cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(vo.getDeviceAlias());

            // 设备类型
            cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(iotProductKey.getProductKey2Name().get(vo.getProductKey()));

            // 商家账号
            cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(vo.getMerchantId());


            // 店铺名称
            cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(vo.getStoreName());

            // 店铺ID
            cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(vo.getStoreId());

            // 总烹饪次数
            cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(vo.getCuisineCounts());

            // 菜谱烹饪
            cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(vo.getRecipeCuisineCount());

            // 手动烹饪
            cell = row.createCell(cellNum++);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(vo.getManualCuisine());
        }
        return wb;
    }

    /**
     * 统计菜谱烹饪数据 recipeId :SuccessCuisineCounts
     *
     * @param req
     * @return
     */
    public Map<String, Long> statisticRecipeCookTask(QueryIngredientReq req) {
        Map<String, Long> map;
        List<RecipeCookTask> list = cookTaskDao.findAll(getRecipeCookTaskStatisticSpecification(req));
        if (StringUtils.isEmpty(req.getStoreId())) {
            map = list.stream().collect(Collectors.groupingBy(RecipeCookTask::getRecipeId, Collectors.counting()));
        } else {
            map = list.stream().filter(r -> req.getStoreId().equals(r.getStoreId())).collect(Collectors.groupingBy(RecipeCookTask::getRecipeId, Collectors.counting()));
        }
        log.info("成功烹饪的菜谱个数为:{}", map.size());
        return map;
    }

    /**
     * 食材搜索
     *
     * @param req
     * @return
     */
    public Specification<RecipeCookTask> getRecipeCookTaskStatisticSpecification(QueryIngredientReq req) {
        return (root, query, cb) -> {
            List<Predicate> predicateList = new ArrayList<>();
            // 菜谱名
            if (!StringUtils.isEmpty(req.getStoreId())) {
                predicateList.add(cb.equal(root.get("storeId").as(String.class), req.getStoreId()));
            }
            // if (!StringUtils.isEmpty(req.getMerchantId())) {
            //     predicateList.add(cb.like(root.get("merchantId").as(String.class), "%" + req.getMerchantId().trim() + "%"));
            // }
            if (req.getStartTime() != null && req.getEndTime() != null) {
                predicateList.add(cb.greaterThanOrEqualTo(root.get(TASK_ADD_TIME_DEVICE).as(Long.class), req.getStartTime()));
                predicateList.add(cb.lessThanOrEqualTo(root.get(TASK_ADD_TIME_DEVICE).as(Long.class), req.getEndTime()));
            }
            predicateList.add(cb.equal(root.get("status").as(Integer.class), RecipeCookTask.STATUS.FINISHED));
            Predicate[] p = new Predicate[predicateList.size()];
            return cb.and(predicateList.toArray(p));
        };
    }

    /**
     * 按店铺id、商家Id(精确查找)设备cgid精确、设备别名、设备类型模糊搜索
     **/
    public Specification<RecipeCookTask> getRecipeCookTaskSpecification(QueryRecipeReq req) {
        return (root, query, cb) -> {
            List<Predicate> predicateList = new ArrayList<>();
            // 菜谱名
            if (!StringUtils.isEmpty(req.getRecipeName())) {
                predicateList.add(cb.like(root.get("recipeName").as(String.class), "%" + req.getRecipeName().trim() + "%"));
            }
            // 菜谱Id
            if (!StringUtils.isEmpty(req.getRecipeId())) {
                predicateList.add(cb.equal(root.get("recipeId").as(String.class), req.getRecipeId().trim()));
            }
            if (!StringUtils.isEmpty(req.getProductKey())) {
                log.info("设备类型:{}", req.getProductKey());
                predicateList.add(cb.equal(root.get("productKey").as(String.class), req.getProductKey().trim()));
            }
            if (!StringUtils.isEmpty(req.getDeviceCgid())) {
                predicateList.add(cb.equal(root.get("deviceCgid").as(String.class), req.getDeviceCgid().trim()));
            }
            if (!StringUtils.isEmpty(req.getStoreId())) {
                predicateList.add(cb.equal(root.get("storeId").as(String.class), req.getStoreId().trim()));
            }

            if (req.getStartTime() != null && req.getEndTime() != null) {

                predicateList.add(cb.greaterThanOrEqualTo(root.get(TASK_ADD_TIME_DEVICE).as(Long.class), req.getStartTime()));
                predicateList.add(cb.lessThanOrEqualTo(root.get(TASK_ADD_TIME_DEVICE).as(Long.class), req.getEndTime()));
            }
            Predicate[] p = new Predicate[predicateList.size()];
            return cb.and(predicateList.toArray(p));
        };
    }



    /**
     * 批量处理数据
     *
     * @param req
     */
    public void patch(PatchReq req) {
        Pageable pageable = new PageRequest(req.getPage() - 1, req.getSize());
        List<RecipeCookTask> tasks = cookTaskDao.findAll(pageable).getContent();
        List<RecipeCookTask> tasksDes = new ArrayList<>(tasks.size());
        SimpleDateFormat df = new SimpleDateFormat("y-M-d");
        for (int i = 0; i < tasks.size(); i++) {
            RecipeCookTask t = tasks.get(i);
            long time = t.getAddTime();
            if (time == 0) {
                time = t.getCreateTime().getTime();
            }
            String dateString = df.format(time);
            try {
                t.setTaskDate(df.parse(dateString));
            } catch (ParseException e) {
                e.printStackTrace();
            }
            tasksDes.add(t);
        }
        cookTaskDao.save(tasksDes);
    }


    /**
     * 候选匹配度相似最高的数据
     * <p>
     * TODO: 当店铺数量较多时,需按一定规则匹配数据
     *
     * @param storeName
     * @return
     */
    public List<String> getCandidateStores(String storeName) {
        if (StringUtils.isBlank(storeName)) {
            return new ArrayList<>(0);
        }
        List<String> allCandidates = storeNames.stream().filter(s -> s.contains(storeName)).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(allCandidates)) {
            return new ArrayList<>(0);
        }
        log.info("所有包含关键字的字符串:   {}", allCandidates);
        List<MatchingDegreeVO> associates = new ArrayList<>();
        for (int i = 0; i < allCandidates.size(); i++) {
            String associateWord = allCandidates.get(i);
            Double matchingDegree = (double) storeName.length() / (double) associateWord.length();
            MatchingDegreeVO tmp = new MatchingDegreeVO();
            tmp.setAssociateWord(associateWord).setMatchingDegree(matchingDegree);
            associates.add(tmp);
        }

        // 当前店铺数较少、全部显示
        List<MatchingDegreeVO> collect = associates.stream().sorted(Comparator.comparingDouble(MatchingDegreeVO::getMatchingDegree).reversed()).collect(Collectors.toList());
        log.info("候选对象及匹配度:{}", collect);
        return collect.stream().map(MatchingDegreeVO::getAssociateWord).collect(Collectors.toList());
    }

}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值