package jnpf.certificate.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageInfo;
import jnpf.authority.service.FtbPermissionOrganizeService;
import jnpf.authority.utils.PermissionsUtils;
import jnpf.base.ActionResult;
import jnpf.certificate.helper.OrganizationHelper;
import jnpf.certificate.mapper.CertificateBusinessLicenseExtMapper;
import jnpf.certificate.mapper.CertificateHygieneLicenseExtMapper;
import jnpf.certificate.mapper.CertificateInstanceItemMapper;
import jnpf.certificate.mapper.CertificateInstanceMapper;
import jnpf.certificate.service.CertificateInstanceService;
import jnpf.certificate.util.CertificateStatusUtils;
import jnpf.certificate.util.StoreTypeUtils;
import jnpf.certificate.util.WorkerStatusUtils;
import jnpf.model.certificate.dto.HealthCertificateStatusDTO;
import jnpf.model.certificate.po.CertificateBusinessLicenseExtEntity;
import jnpf.model.certificate.po.CertificateHygieneLicenseExtEntity;
import jnpf.model.certificate.po.CertificateInstanceEntity;
import jnpf.model.certificate.po.CertificateInstanceItemEntity;
import jnpf.model.certificate.req.CertificateInstanceAddReq;
import jnpf.model.certificate.req.CertificateHealthManageQueryReq;
import jnpf.model.certificate.req.CertificateInstanceItemReq;
import jnpf.model.certificate.req.CertificateInstanceQueryReq;
import jnpf.model.certificate.req.CertificateInstanceUpdateReq;
import jnpf.model.certificate.req.CertificateStoreManageQueryReq;
import jnpf.model.certificate.req.CertificateStoreDashboardReq;
import jnpf.model.certificate.req.CertificateSyncHealthReq;
import jnpf.model.certificate.vo.CertificateHealthDashboardStatusStatVO;
import jnpf.model.certificate.vo.CertificateHealthManageVO;
import jnpf.model.certificate.vo.CertificateInstanceItemVO;
import jnpf.model.certificate.vo.CertificateInstanceVO;
import jnpf.model.certificate.vo.CertificateStoreManageVO;
import jnpf.model.certificate.vo.CertificateStoreDashboardVO;
import jnpf.model.certificate.vo.CertificateStoreCustomStatusBarVO;
import jnpf.model.certificate.vo.CertificateStoreCustomStatusPieVO;
import jnpf.model.certificate.vo.CertificateStoreCustomStatusTableVO;
import jnpf.model.certificate.vo.CertificateTypeOptionVO;
import jnpf.model.certificate.vo.app.CertificateAppStoreRiskVO;
import jnpf.model.certificate.vo.app.HealthCertificateDetailVO;
import jnpf.model.enums.StaffWorkerStatus;
import jnpf.model.personnels.po.FtbPersonnelsStaffRegistrationFormData;
import jnpf.model.storecertificatephoto.po.StoreCertificatePhotoEntity;
import jnpf.model.warningnotice.enums.CertificateTypeEnum;
import jnpf.model.warningnotice.vo.WarningNoticeVO;
import jnpf.permission.UserApi;
import jnpf.permission.V2OrganizeApi;
import jnpf.permission.V2PositionApi;
import jnpf.permission.dto.v2.user.QueryUserBaseRelationDTO;
import jnpf.permission.eum.v2.OrganizeCategoryEnums;
import jnpf.permission.vo.v2.organzie.OrganizeBaseInfoVO;
import jnpf.permission.vo.v2.organzie.OrganizeGeneralDetailVO;
import jnpf.permission.vo.v2.position.PositionBaseInfoVO;
import jnpf.permission.vo.v2.user.UserBaseRelationSimpleVO;
import jnpf.permission.vo.v2.user.UserBoundVO;
import jnpf.personnels.service.FtbPersonnelsStaffRegistrationFormDataService;
import jnpf.storecertificatephoto.mapper.StoreCertificatePhotoMapper;
import jnpf.storecertificatephoto.service.WarningNoticeService;
import jnpf.util.FtbUtil;
import jnpf.util.ServiceException;
import jnpf.util.StringUtil;
import jnpf.util.UserProvider;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;


@Service
@Slf4j
public class CertificateInstanceServiceImpl extends ServiceImpl<CertificateInstanceMapper, CertificateInstanceEntity>
        implements CertificateInstanceService {

    private static final int STATUS_MISSING = 1;
    private static final int STATUS_NORMAL = 4;
    private static final int STATUS_NEAR_EXPIRE = 3;
    private static final int STATUS_EXPIRED = 2;

    private static final int BOARD_STATUS_MISSING = 1;
    private static final int BOARD_STATUS_NEAR_EXPIRE = 3;
    private static final int BOARD_STATUS_EXPIRED = 2;
    private static final int BOARD_STATUS_NORMAL = 4;

    private static final int SUBJECT_TYPE_EMPLOYEE = 1;
    private static final int SUBJECT_TYPE_STORE = 3;
    private static final int NEAR_EXPIRE_DAYS = 30;
    @Autowired
    private CertificateInstanceItemMapper certificateInstanceItemMapper;
    @Autowired
    private CertificateBusinessLicenseExtMapper certificateBusinessLicenseExtMapper;
    @Autowired
    private CertificateHygieneLicenseExtMapper certificateHygieneLicenseExtMapper;
    @Autowired
    private FtbPermissionOrganizeService ftbPermissionOrganizeService;
    @Autowired
    private StoreCertificatePhotoMapper storeCertificatePhotoMapper;
    @Autowired
    private PermissionsUtils permissionsUtils;
    @Autowired
    private WarningNoticeService warningNoticeService;
    @Autowired
    private V2OrganizeApi v2OrganizeApi;
    @Autowired
    private V2PositionApi v2PositionApi;
    @Autowired
    private UserApi userApi;
    @Autowired
    private OrganizationHelper organizationHelper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void add(CertificateInstanceAddReq req) {
        ServiceException.notNull(req, "请求参数不能为空");
        validateSaveReq(req);

        Date issueDate = parseDate(req.getIssueDate(), "发证日期");
        Date expireDate = parseDate(req.getExpireDate(), "到期日期");
        Integer status = calculateStatus(req.getIsLongTerm(), expireDate);

        CertificateInstanceEntity entity = req.convert(issueDate, expireDate, status);
        normalizeSaveEntity(entity);
        baseMapper.insert(entity);

        saveItems(entity.getId(), req.getItemList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(CertificateInstanceUpdateReq req) {
        ServiceException.notNull(req, "请求参数不能为空");
        ServiceException.isTrue(StrUtil.isNotBlank(req.getId()), "实例ID不能为空");
        validateSaveReq(req);

        CertificateInstanceEntity dbEntity = queryActiveEntity(req.getId().trim());
        Date issueDate = parseDate(req.getIssueDate(), "发证日期");
        Date expireDate = parseDate(req.getExpireDate(), "到期日期");
        Integer status = calculateStatus(req.getIsLongTerm(), expireDate);

        CertificateInstanceEntity entity = req.convert(issueDate, expireDate, status);
        entity.setId(dbEntity.getId());
        entity.setEnabledMark(dbEntity.getEnabledMark());
        normalizeSaveEntity(entity);
        baseMapper.updateById(entity);

        LambdaQueryWrapper<CertificateInstanceItemEntity> itemDeleteWrapper = Wrappers.lambdaQuery();
        itemDeleteWrapper.eq(CertificateInstanceItemEntity::getInstanceId, dbEntity.getId());
        certificateInstanceItemMapper.delete(itemDeleteWrapper);
        saveItems(dbEntity.getId(), req.getItemList());
    }

    @Override
    public CertificateInstanceVO queryInfo(String id) {
        ServiceException.isTrue(StrUtil.isNotBlank(id), "实例ID不能为空");
        CertificateInstanceEntity entity = queryActiveEntity(id.trim());
        CertificateInstanceVO vo = CertificateInstanceVO.convert(entity);
        vo.setStatus(calculateStatus(entity.getIsLongTerm(), entity.getExpireDate()));
        fillCertificateExtInfo(vo, entity);

        LambdaQueryWrapper<CertificateInstanceItemEntity> itemWrapper = Wrappers.lambdaQuery();
        itemWrapper.eq(CertificateInstanceItemEntity::getInstanceId, entity.getId());
        itemWrapper.eq(CertificateInstanceItemEntity::getEnabledMark, 0);
        itemWrapper.orderByAsc(CertificateInstanceItemEntity::getSorts);
        List<CertificateInstanceItemEntity> itemEntities = certificateInstanceItemMapper.selectList(itemWrapper);
        if (CollUtil.isNotEmpty(itemEntities)) {
            vo.setItemList(itemEntities.stream().map(CertificateInstanceItemVO::convert).collect(Collectors.toList()));
        }
        return vo;
    }

    private void fillCertificateExtInfo(CertificateInstanceVO vo, CertificateInstanceEntity entity) {
        if (vo == null || entity == null) {
            return;
        }
        String certificateType = StrUtil.trim(entity.getCertificateType());
        if (StrUtil.isBlank(certificateType)) {
            return;
        }
        if (StrUtil.equalsIgnoreCase(certificateType, CertificateTypeEnum.BUSINESS_LICENSE.getType())) {
            LambdaQueryWrapper<CertificateBusinessLicenseExtEntity> queryWrapper = Wrappers.lambdaQuery();
            queryWrapper.eq(CertificateBusinessLicenseExtEntity::getInstanceId, entity.getId());
            queryWrapper.eq(CertificateBusinessLicenseExtEntity::getEnabledMark, 0);
            queryWrapper.orderByDesc(CertificateBusinessLicenseExtEntity::getLastModifyTime);
            queryWrapper.orderByDesc(CertificateBusinessLicenseExtEntity::getCreatorTime);
            List<CertificateBusinessLicenseExtEntity> extList = certificateBusinessLicenseExtMapper.selectList(queryWrapper);
            if (CollUtil.isEmpty(extList)) {
                return;
            }
            CertificateBusinessLicenseExtEntity ext = extList.get(0);
            vo.setCompanyName(ext.getCompanyName());
            vo.setCompanyAddress(ext.getCompanyAddress());
            vo.setLegalRepresentative(ext.getLegalRepresentative());
            vo.setEstablishDate(ext.getEstablishDate());
            vo.setBusinessScope(ext.getBusinessScope());
            return;
        }
        if (StrUtil.equalsIgnoreCase(certificateType, CertificateTypeEnum.HYGIENE_LICENSE.getType())) {
            LambdaQueryWrapper<CertificateHygieneLicenseExtEntity> queryWrapper = Wrappers.lambdaQuery();
            queryWrapper.eq(CertificateHygieneLicenseExtEntity::getInstanceId, entity.getId());
            queryWrapper.eq(CertificateHygieneLicenseExtEntity::getEnabledMark, 0);
            queryWrapper.orderByDesc(CertificateHygieneLicenseExtEntity::getLastModifyTime);
            queryWrapper.orderByDesc(CertificateHygieneLicenseExtEntity::getCreatorTime);
            List<CertificateHygieneLicenseExtEntity> extList = certificateHygieneLicenseExtMapper.selectList(queryWrapper);
            if (CollUtil.isEmpty(extList)) {
                return;
            }
            CertificateHygieneLicenseExtEntity ext = extList.get(0);
            vo.setBusinessProject(ext.getBusinessProject());
        }
    }

    @Override
    public PageInfo<CertificateInstanceVO> queryPage(CertificateInstanceQueryReq req) {
        CertificateInstanceQueryReq queryReq = req == null ? new CertificateInstanceQueryReq() : req;
        long current = queryReq.getCurrentPage() == null || queryReq.getCurrentPage() <= 0 ? 1L : queryReq.getCurrentPage();
        long pageSize = queryReq.getPageSize() == null || queryReq.getPageSize() <= 0 ? 10L : queryReq.getPageSize();

        Page<CertificateInstanceEntity> page = new Page<>(current, pageSize);
        LambdaQueryWrapper<CertificateInstanceEntity> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(CertificateInstanceEntity::getEnabledMark, 0);

        if (StrUtil.isNotBlank(queryReq.getType())) {
            Optional<CertificateTypeEnum> certificateTypeEnumOptional = CertificateTypeEnum.getByType(queryReq.getType());
            ServiceException.isTrue(certificateTypeEnumOptional.isPresent(), "证照类型不正确");
            wrapper.eq(CertificateInstanceEntity::getCertificateType, certificateTypeEnumOptional.get().getType());
        }
        wrapper.eq(queryReq.getSubjectType() != null, CertificateInstanceEntity::getSubjectType, queryReq.getSubjectType());
        wrapper.eq(StrUtil.isNotBlank(queryReq.getSubjectId()), CertificateInstanceEntity::getSubjectId, StrUtil.trim(queryReq.getSubjectId()));

        Date today = DateUtil.beginOfDay(DateUtil.date());
        Date nearExpireLimit = DateUtil.endOfDay(DateUtil.offsetDay(today, NEAR_EXPIRE_DAYS));
        if (queryReq.getStatus() != null) {
            if (queryReq.getStatus() == STATUS_NORMAL) {
                wrapper.and(w -> w.eq(CertificateInstanceEntity::getIsLongTerm, 1)
                        .or()
                        .isNull(CertificateInstanceEntity::getExpireDate)
                        .or()
                        .gt(CertificateInstanceEntity::getExpireDate, nearExpireLimit));
            } else if (queryReq.getStatus() == STATUS_NEAR_EXPIRE) {
                wrapper.eq(CertificateInstanceEntity::getIsLongTerm, 0);
                wrapper.ge(CertificateInstanceEntity::getExpireDate, today);
                wrapper.le(CertificateInstanceEntity::getExpireDate, nearExpireLimit);
            } else if (queryReq.getStatus() == STATUS_EXPIRED) {
                wrapper.eq(CertificateInstanceEntity::getIsLongTerm, 0);
                wrapper.isNotNull(CertificateInstanceEntity::getExpireDate);
                wrapper.lt(CertificateInstanceEntity::getExpireDate, today);
            }
        }

        Date expireStart = parseDate(queryReq.getExpireStart(), "到期开始日期");
        Date expireEnd = parseDate(queryReq.getExpireEnd(), "到期结束日期");
        if (expireStart != null) {
            wrapper.ge(CertificateInstanceEntity::getExpireDate, DateUtil.beginOfDay(expireStart));
        }
        if (expireEnd != null) {
            wrapper.le(CertificateInstanceEntity::getExpireDate, DateUtil.endOfDay(expireEnd));
        }

        if (queryReq.getDaysToExpireLte() != null) {
            Date limitDate = DateUtil.endOfDay(DateUtil.offsetDay(today, queryReq.getDaysToExpireLte()));
            wrapper.eq(CertificateInstanceEntity::getIsLongTerm, 0);
            wrapper.isNotNull(CertificateInstanceEntity::getExpireDate);
            wrapper.ge(CertificateInstanceEntity::getExpireDate, today);
            wrapper.le(CertificateInstanceEntity::getExpireDate, limitDate);
        }

        if (StrUtil.isNotBlank(queryReq.getKeyword())) {
            String keyword = StrUtil.trim(queryReq.getKeyword());
            wrapper.and(w -> w.like(CertificateInstanceEntity::getCertificateNo, keyword)
                    .or()
                    .like(CertificateInstanceEntity::getSubjectId, keyword));
        }

        wrapper.orderByDesc(CertificateInstanceEntity::getLastModifyTime);
        wrapper.orderByDesc(CertificateInstanceEntity::getCreatorTime);

        Page<CertificateInstanceEntity> queryPage = baseMapper.selectPage(page, wrapper);
        List<CertificateInstanceVO> voList = queryPage.getRecords().stream()
                .map(CertificateInstanceVO::convert)
                .collect(Collectors.toList());
        for (CertificateInstanceVO vo : voList) {
            vo.setType(vo.getType() == null ? null : vo.getType().trim());
            vo.setStatus(calculateStatus(vo.getIsLongTerm(), vo.getExpireDate()));
        }

        PageInfo<CertificateInstanceVO> pageInfo = new PageInfo<>();
        pageInfo.setList(voList);
        pageInfo.setPageNum((int) queryPage.getCurrent());
        pageInfo.setPageSize((int) queryPage.getSize());
        pageInfo.setTotal((int) queryPage.getTotal());
        return pageInfo;
    }

    @Override
    public PageInfo<CertificateHealthManageVO> queryHealthPage(CertificateHealthManageQueryReq req) {
        CertificateHealthManageQueryReq queryReq = req == null ? new CertificateHealthManageQueryReq() : req;
        long current = queryReq.getCurrentPage() == null || queryReq.getCurrentPage() <= 0 ? 1L : queryReq.getCurrentPage();
        long pageSize = queryReq.getPageSize() == null || queryReq.getPageSize() <= 0 ? 10L : queryReq.getPageSize();

        //无法查询离职员工
        String workerStatus = buildWorkStatus(queryReq);
        if(StaffWorkerStatus.RESIGNED.getCode().equals(workerStatus)){
            return new PageInfo<>(Collections.emptyList());
        }
        //装载非离职员工
        if(StringUtil.isBlank(workerStatus)){
            queryReq.setWorkerStatusCollection(Stream.of(StaffWorkerStatus.values())
                            .filter(w->!StaffWorkerStatus.RESIGNED.equals(w))
                            .map(StaffWorkerStatus::getCode)
                    .collect(Collectors.toSet()));
        }
        Map<String, UserBaseRelationSimpleVO> userBaseVoByUserId = buildQueryUserIds(queryReq);
        if(org.springframework.util.CollectionUtils.isEmpty(userBaseVoByUserId)){
            return new PageInfo<>(Collections.emptyList());
        }
        Page<CertificateHealthManageVO> page = new Page<>(current, pageSize);
        Page<CertificateHealthManageVO> queryPage = baseMapper.queryHealthManagePage(
                page,
                queryReq,
                NEAR_EXPIRE_DAYS
        );

        fillCertificateHealthManageVO(queryPage.getRecords(),userBaseVoByUserId);

        PageInfo<CertificateHealthManageVO> pageInfo = new PageInfo<>();
        pageInfo.setList(queryPage.getRecords() == null ? Collections.emptyList() : queryPage.getRecords());
        pageInfo.setPageNum((int) queryPage.getCurrent());
        pageInfo.setPageSize((int) queryPage.getSize());
        pageInfo.setTotal((int) queryPage.getTotal());
        return pageInfo;
    }

    private Map<String, UserBaseRelationSimpleVO> buildQueryUserIds(CertificateHealthManageQueryReq queryReq) {
        String workerStatus = queryReq.getWorkerStatus();
        String positionId = queryReq.getPositionId();
        List<String> orgIds = queryReq.getOrgIds();
        String keyword = queryReq.getKeyword();

        QueryUserBaseRelationDTO dto = new QueryUserBaseRelationDTO();
        if(StringUtil.isNotBlank(workerStatus)){
            dto.setWorkerStatusCollection(List.of(workerStatus));
        }else{
            dto.setWorkerStatusCollection(queryReq.getWorkerStatusCollection());
        }
        dto.setPositionId(positionId);
        dto.setKeyword(keyword);
        dto.setOrgIds(orgIds);
        Map<String, UserBaseRelationSimpleVO> userBaseVOByUserId = organizationHelper.getUserBaseRelationMapByUserIds(dto);
        if(org.springframework.util.CollectionUtils.isEmpty(userBaseVOByUserId)){
            return Collections.emptyMap();
        }
        queryReq.setUserIds(userBaseVOByUserId.keySet());
        return userBaseVOByUserId;
    }

    private String buildWorkStatus(CertificateHealthManageQueryReq req) {
        if(Objects.isNull(req)){
            return "";
        }
        String workerStatus = req.getWorkerStatus();
        if(StringUtil.isBlank(workerStatus)){
            return "";
        }
        return workerStatus;
    }

    private void fillCertificateHealthManageVO(List<CertificateHealthManageVO> records,
                                               Map<String, UserBaseRelationSimpleVO> userBaseVoByUserId) {
        if(CollectionUtils.isEmpty(records)){
            return;
        }
        for (CertificateHealthManageVO certificateHealthManageVO:records){
            UserBaseRelationSimpleVO userBaseRelationSimpleVO = userBaseVoByUserId.get(certificateHealthManageVO.getUserId());
            if(Objects.isNull(userBaseRelationSimpleVO)){
                continue;
            }
            certificateHealthManageVO.setOrgId(userBaseRelationSimpleVO.getOrganizeId());
            certificateHealthManageVO.setOrgName(userBaseRelationSimpleVO.getOrganizeName());
            certificateHealthManageVO.setPositionId(userBaseRelationSimpleVO.getPositionId());
            certificateHealthManageVO.setPositionName(userBaseRelationSimpleVO.getPositionName());
            certificateHealthManageVO.setCertificateStatusName(CertificateStatusUtils.buildStatusName(certificateHealthManageVO.getCertificateStatus()));
            certificateHealthManageVO.setWorkerStatus(userBaseRelationSimpleVO.getWorkerStatus());
            certificateHealthManageVO.setWorkerStatusName(WorkerStatusUtils.buildStatusName(userBaseRelationSimpleVO.getWorkerStatus()));
            certificateHealthManageVO.setPhone(userBaseRelationSimpleVO.getMobilePhone());
            certificateHealthManageVO.setName(userBaseRelationSimpleVO.getUserName());
        }
    }

    @Override
    public PageInfo<CertificateStoreManageVO> queryStorePage(CertificateStoreManageQueryReq req) {
        CertificateStoreManageQueryReq queryReq = req == null ? new CertificateStoreManageQueryReq() : req;
        long current = queryReq.getCurrentPage() == null || queryReq.getCurrentPage() <= 0 ? 1L : queryReq.getCurrentPage();
        long pageSize = queryReq.getPageSize() == null || queryReq.getPageSize() <= 0 ? 10L : queryReq.getPageSize();

        PageInfo<CertificateStoreManageVO> emptyPageInfo = new PageInfo<>();
        emptyPageInfo.setList(Collections.emptyList());
        emptyPageInfo.setPageNum((int) current);
        emptyPageInfo.setPageSize((int) pageSize);
        emptyPageInfo.setTotal(0);

        Set<String> orgIds = new HashSet<>(queryReq.getOrgIds());
        Map<String, OrganizeBaseInfoVO> organizeBaseInfoVOMap;
        String storeTag = queryReq.getStoreTag();
        if(StringUtil.isNotBlank(storeTag)){
            organizeBaseInfoVOMap = organizationHelper.buildBaseOrganizeVO(queryReq.getOrgIds(),storeTag,UserProvider.getUser().getTenantId());
            Map<String, OrganizeBaseInfoVO> finalOrganizeBaseInfoVOMap = organizeBaseInfoVOMap;
            orgIds.removeIf(oi->!finalOrganizeBaseInfoVOMap.containsKey(oi));
            orgIds.addAll(organizeBaseInfoVOMap.keySet());
        } else {
            organizeBaseInfoVOMap = Collections.emptyMap();
        }

        //查询的组织id列表为空，且想要查询的组织id列表不为空，说明未查询到
        if(CollUtil.isEmpty(orgIds) && CollUtil.isNotEmpty(queryReq.getOrgIds())){
            return emptyPageInfo;
        }

        queryReq.setOrgIds(orgIds);
        buildTemplateId(queryReq);
        Page<CertificateStoreManageVO> page = new Page<>(current, pageSize);
        Page<CertificateStoreManageVO> queryPage = baseMapper.selectCertificateStoreManageVOPage(page,queryReq);
        List<CertificateStoreManageVO> certificateStoreManageVOS = queryPage.getRecords();
        if(CollUtil.isEmpty(certificateStoreManageVOS)){
            return emptyPageInfo;
        }

        Set<String> needQueryOrgIds = new HashSet<>();
        for (CertificateStoreManageVO certificateStoreManageVO:certificateStoreManageVOS){
            String orgId = certificateStoreManageVO.getOrgId();
            if(StringUtil.isBlank(orgId)){
                continue;
            }

            OrganizeBaseInfoVO organizeBaseInfoVO = organizeBaseInfoVOMap.get(orgId);
            if(Objects.isNull(organizeBaseInfoVO)){
                needQueryOrgIds.add(orgId);
                continue;
            }
            fillCertificateStoreManageVO(certificateStoreManageVO,organizeBaseInfoVO);
        }

        if(CollUtil.isEmpty(needQueryOrgIds)){
            PageInfo<CertificateStoreManageVO> pageInfo = new PageInfo<>();
            pageInfo.setList(certificateStoreManageVOS);
            pageInfo.setPageNum((int) current);
            pageInfo.setPageSize((int) pageSize);
            pageInfo.setTotal(queryPage.getTotal());
            return pageInfo;
        }

        organizeBaseInfoVOMap = organizationHelper.buildBaseOrganizeVO(needQueryOrgIds,storeTag,UserProvider.getUser().getTenantId());
        fillCertificateStoreManageVOs(certificateStoreManageVOS,organizeBaseInfoVOMap);

        PageInfo<CertificateStoreManageVO> pageInfo = new PageInfo<>();
        pageInfo.setList(certificateStoreManageVOS);
        pageInfo.setPageNum((int) current);
        pageInfo.setPageSize((int) pageSize);
        pageInfo.setTotal(queryPage.getTotal());
        return pageInfo;
    }

    private void buildTemplateId(CertificateStoreManageQueryReq queryReq) {
        String certificateType = queryReq.getCertificateType();
        Optional<CertificateTypeEnum> certificateTypeEnumOptional = CertificateTypeEnum.getByType(certificateType);
        if(certificateTypeEnumOptional.isEmpty()){
            throw new ServiceException("证照类型不能为空!");
        }
        CertificateTypeEnum certificateTypeEnum = certificateTypeEnumOptional.get();
        queryReq.setCertificateType(certificateTypeEnum.getType());
        if(certificateTypeEnum == CertificateTypeEnum.STORE_CUSTOM_CERTIFICATE){
            queryReq.setTemplateId(certificateType);
        }
    }

    private void fillCertificateStoreManageVOs(List<CertificateStoreManageVO> certificateStoreManageVOS, Map<String, OrganizeBaseInfoVO> organizeBaseInfoVOMap) {
        if(CollUtil.isEmpty(certificateStoreManageVOS) || CollUtil.isEmpty(organizeBaseInfoVOMap)){
            return;
        }
        for (CertificateStoreManageVO certificateStoreManageVO : certificateStoreManageVOS){
            OrganizeBaseInfoVO organizeBaseInfoVO = organizeBaseInfoVOMap.get(certificateStoreManageVO.getOrgId());
            fillCertificateStoreManageVO(certificateStoreManageVO, organizeBaseInfoVO);
        }
    }

    private void fillCertificateStoreManageVO(CertificateStoreManageVO certificateStoreManageVO, OrganizeBaseInfoVO organizeBaseInfoVO) {
        if(Objects.isNull(certificateStoreManageVO) || Objects.isNull(organizeBaseInfoVO)){
            return;
        }
        certificateStoreManageVO.setOrgName(organizeBaseInfoVO.getName());
        certificateStoreManageVO.setOrgCategory(organizeBaseInfoVO.getOrganizeCategory());
        certificateStoreManageVO.setOrgCategoryName(organizeBaseInfoVO.getOrganizeCategoryEnums().getDescription());
        certificateStoreManageVO.setParentOrgName(organizeBaseInfoVO.getParentName());
        certificateStoreManageVO.setLeaderName(organizeBaseInfoVO.getLeaderName());
        certificateStoreManageVO.setStoreTypeName(StoreTypeUtils.buildStoreType(organizeBaseInfoVO.getStoreTag()));
        certificateStoreManageVO.setCertificateStatusName(CertificateStatusUtils.buildStatusName(certificateStoreManageVO.getCertificateStatus()));
        certificateStoreManageVO.setBusinessTerm(buildBusinessTerm(certificateStoreManageVO));
    }

    private String buildBusinessTerm(CertificateStoreManageVO certificateStoreManageVO) {
        if(Objects.isNull(certificateStoreManageVO)){
            return "-";
        }
        Integer isLongTerm = certificateStoreManageVO.getIsLongTerm();
        if(Objects.nonNull(isLongTerm) && isLongTerm == 1){
            return "长期";
        }
        String startDate = DateUtil.format(certificateStoreManageVO.getIssueDate(), "yyyy-MM-dd");
        String endDate = DateUtil.format(certificateStoreManageVO.getExpireDate(), "yyyy-MM-dd");
        if(StringUtil.isBlank(startDate) || StringUtil.isBlank(endDate)){
            return "-";
        }
        return startDate + "至" + endDate;
    }

    @Override
    public List<CertificateTypeOptionVO> queryCertificateTypeList() {
        List<CertificateTypeOptionVO> result = new ArrayList<>();
        List<CertificateTypeEnum> defaultTypeList = Arrays.asList(
                CertificateTypeEnum.HEALTH_CERTIFICATE,
                CertificateTypeEnum.BUSINESS_LICENSE,
                CertificateTypeEnum.HYGIENE_LICENSE
        );
        for (CertificateTypeEnum certificateType : defaultTypeList) {
            CertificateTypeOptionVO optionVO = new CertificateTypeOptionVO();
            optionVO.setLabel(certificateType.getLabel());
            optionVO.setKey(certificateType.getType());
            optionVO.setType(certificateType.getType());
            result.add(optionVO);
        }

        LambdaQueryWrapper<StoreCertificatePhotoEntity> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(StoreCertificatePhotoEntity::getEnabledMark, 0);
        queryWrapper.eq(StoreCertificatePhotoEntity::getStatus, 1);
        List<StoreCertificatePhotoEntity> templateList = storeCertificatePhotoMapper.selectList(queryWrapper);
        if (CollUtil.isEmpty(templateList)) {
            return result;
        }

        for (StoreCertificatePhotoEntity template : templateList) {
            if (template == null || StrUtil.isBlank(template.getId()) || StrUtil.isBlank(template.getCertificateName())) {
                continue;
            }
            CertificateTypeOptionVO optionVO = new CertificateTypeOptionVO();
            optionVO.setLabel(StrUtil.trim(template.getCertificateName()));
            optionVO.setKey(StrUtil.trim(template.getId()));
            optionVO.setType(CertificateTypeEnum.STORE_CUSTOM_CERTIFICATE.getType());
            result.add(optionVO);
        }
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(String id) {
        ServiceException.isTrue(StrUtil.isNotBlank(id), "实例ID不能为空");
        CertificateInstanceEntity entity = queryActiveEntity(id.trim());
        entity.setEnabledMark(1);
        baseMapper.updateById(entity);

        CertificateInstanceItemEntity itemUpdateEntity = new CertificateInstanceItemEntity();
        itemUpdateEntity.setEnabledMark(1);
        LambdaQueryWrapper<CertificateInstanceItemEntity> itemWrapper = Wrappers.lambdaQuery();
        itemWrapper.eq(CertificateInstanceItemEntity::getInstanceId, entity.getId());
        itemWrapper.eq(CertificateInstanceItemEntity::getEnabledMark, 0);
        certificateInstanceItemMapper.update(itemUpdateEntity, itemWrapper);
    }

    @Override
    public void saveHealthCertificate(String userId, String healthCertificate, String startHealthDate, String endHealthDate){
        log.error("saveHealthCertificate info userId:{},healthCertificate:{},startHealthDate:{},endHealthDate:{}",userId,healthCertificate,startHealthDate,endHealthDate);
        CertificateInstanceEntity entity = buildEntity(userId, healthCertificate, startHealthDate, endHealthDate);
        int r = updateByWrapper(entity);
        if(r>0){
            return;
        }
        try {
            entity.setId(FtbUtil.getId());
            entity.setCreatorTime(new Date());
            entity.setCreatorUserId(UserProvider.getLoginUserId());
            baseMapper.insert(entity);
        }catch (DuplicateKeyException duplicateKeyException){
            r = updateByWrapper(entity);
            if(r<=0){
                log.error("insert CertificateInstanceEntity fail.and update fail");
            }
        }
    }

    @Override
    public void deleteHealthCertificate(String userId) {
        baseMapper.delete(new LambdaQueryWrapper<CertificateInstanceEntity>()
                .eq(CertificateInstanceEntity::getSubjectId,userId)
                .eq(CertificateInstanceEntity::getSubjectType,1)
                .eq(CertificateInstanceEntity::getCertificateType,CertificateTypeEnum.HEALTH_CERTIFICATE.getType())
                .eq(CertificateInstanceEntity::getTemplateId,""));
    }

    private int updateByWrapper(CertificateInstanceEntity entity) {
        return baseMapper.update(null,new LambdaUpdateWrapper<CertificateInstanceEntity>()
                .eq(CertificateInstanceEntity::getCertificateType, CertificateTypeEnum.HEALTH_CERTIFICATE.getType())
                .eq(CertificateInstanceEntity::getSubjectId, entity.getSubjectId())
                .set(CertificateInstanceEntity::getCertificateImage,entity.getCertificateImage())
                .set(CertificateInstanceEntity::getIssueDate,entity.getIssueDate())
                .set(CertificateInstanceEntity::getExpireDate,entity.getExpireDate())
                .set(CertificateInstanceEntity::getExpireTimestamp,entity.getExpireTimestamp())
                .set(CertificateInstanceEntity::getStatus,entity.getStatus())
                .set(CertificateInstanceEntity::getLastModifyTime,entity.getLastModifyTime())
                .set(CertificateInstanceEntity::getLastModifyUserId,entity.getLastModifyUserId()));
    }

    @Override
    public Optional<HealthCertificateDetailVO> getHealthCertificateDetail(String userId) {
        CertificateInstanceEntity certificateInstanceEntity = baseMapper.selectOne(new LambdaUpdateWrapper<CertificateInstanceEntity>()
                .eq(CertificateInstanceEntity::getCertificateType, CertificateTypeEnum.HEALTH_CERTIFICATE.getType())
                .eq(CertificateInstanceEntity::getSubjectId, userId));
        if(Objects.isNull(certificateInstanceEntity)){
            return Optional.empty();
        }
        return Optional.of(new HealthCertificateDetailVO(certificateInstanceEntity.getSubjectId(),
                certificateInstanceEntity.getId(),
                certificateInstanceEntity.getCertificateImage(),
                certificateInstanceEntity.getIssueDate(),
                certificateInstanceEntity.getExpireDate(),
                certificateInstanceEntity.getStatus()));
    }

    @Override
    public List<HealthCertificateDetailVO> getHealthCertificateDetails(Collection<String> userIds) {
        if(CollectionUtils.isEmpty(userIds)){
            return Collections.emptyList();
        }
        return baseMapper.selectList(new LambdaUpdateWrapper<CertificateInstanceEntity>().in(CertificateInstanceEntity::getSubjectId,userIds)
                .eq(CertificateInstanceEntity::getCertificateType, CertificateTypeEnum.HEALTH_CERTIFICATE.getType()))
                .stream()
                .map(certificateInstanceEntity->new HealthCertificateDetailVO(certificateInstanceEntity.getSubjectId(),
                        certificateInstanceEntity.getId(),
                        certificateInstanceEntity.getCertificateImage(),
                        certificateInstanceEntity.getIssueDate(),
                        certificateInstanceEntity.getExpireDate(),
                        certificateInstanceEntity.getStatus()))
                .collect(Collectors.toList());
    }

    @Override
    public List<String> getUserIdsByStatus(Integer status,String certificateType) {
        if(Objects.isNull(status) && StringUtil.isBlank(certificateType)){
            return Collections.emptyList();
        }
        return baseMapper.selectList(new LambdaUpdateWrapper<CertificateInstanceEntity>()
                        .eq(CertificateInstanceEntity::getCertificateType, certificateType)
                        .eq(CertificateInstanceEntity::getStatus, status))
                .stream()
                .map(CertificateInstanceEntity::getSubjectId)
                .distinct()
                .collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int batchInitHealthCertificate(Collection<String> userIds) {
        if (CollectionUtils.isEmpty(userIds)) {
            return 0;
        }

        List<String> targetUserIds = userIds.stream()
                .filter(StrUtil::isNotBlank)
                .map(StrUtil::trim)
                .distinct()
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(targetUserIds)) {
            return 0;
        }

        LambdaQueryWrapper<CertificateInstanceEntity> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.select(CertificateInstanceEntity::getSubjectId);
        queryWrapper.eq(CertificateInstanceEntity::getCertificateType, CertificateTypeEnum.HEALTH_CERTIFICATE.getType());
        queryWrapper.in(CertificateInstanceEntity::getSubjectId, targetUserIds);
        List<CertificateInstanceEntity> existList = baseMapper.selectList(queryWrapper);
        Set<String> existUserIds = existList.stream()
                .map(CertificateInstanceEntity::getSubjectId)
                .filter(StrUtil::isNotBlank)
                .map(StrUtil::trim)
                .collect(Collectors.toSet());

        List<String> needInsertUserIds = targetUserIds.stream()
                .filter(userId -> !existUserIds.contains(userId))
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(needInsertUserIds)) {
            return 0;
        }
        List<CertificateInstanceEntity> initEntities = needInsertUserIds.stream()
                .map(userId -> {
                    CertificateInstanceEntity entity = new CertificateInstanceEntity();
                    entity.setId(FtbUtil.getId());
                    entity.setCertificateType(CertificateTypeEnum.HEALTH_CERTIFICATE.getType());
                    entity.setSubjectType(SUBJECT_TYPE_EMPLOYEE);
                    entity.setSubjectId(userId);
                    entity.setStatus(STATUS_MISSING);
                    entity.setEnabledMark(0);
                    return entity;
                })
                .collect(Collectors.toList());
        return baseMapper.batchInitHealthCertificate(initEntities);
    }

    public CertificateInstanceEntity buildEntity(String userId, String healthCertificate, String startHealthDate, String endHealthDate) {
        CertificateInstanceEntity entity = new CertificateInstanceEntity();
        entity.setCertificateType(CertificateTypeEnum.HEALTH_CERTIFICATE.getType());
        entity.setSubjectType(SUBJECT_TYPE_EMPLOYEE);
        entity.setSubjectId(userId);
        entity.setCertificateImage(healthCertificate);
        int status = 1;//默认缺失
        if(StringUtil.isNotBlank(startHealthDate)){
            entity.setIssueDate(parseDate(startHealthDate,"startHealthDate"));
        }
        if(StringUtil.isNotBlank(endHealthDate)){
            WarningNoticeVO warningNoticeVO = warningNoticeService.queryByType(CertificateTypeEnum.HEALTH_CERTIFICATE.getType());
            Date expireDate = parseDate(endHealthDate,"endHealthDate");
            entity.setExpireDate(expireDate);
            entity.setExpireTimestamp(expireDate.getTime());
            int nearExpireDays = NEAR_EXPIRE_DAYS;
            if(Objects.nonNull(warningNoticeVO)){
                nearExpireDays = warningNoticeVO.getExpiryReminderDays();
            }
            status = calculateStatus(0,entity.getExpireDate(),nearExpireDays);
        }

        entity.setIsLongTerm(0);
        entity.setStatus(status);
        entity.setEnabledMark(0);
        entity.setLastModifyTime(new Date());
        entity.setLastModifyUserId(UserProvider.getLoginUserId());
        return entity;
    }

    @Override
    public CertificateStoreDashboardVO storeCertificateDashboard(CertificateStoreDashboardReq req) {
        ServiceException.notNull(req, "请求参数不能为空");

        List<String> storeIdFilter = req.getStoreIds();
        String storeTypeFilter = StrUtil.trim(req.getStoreType());
        String certificateTypeText = StrUtil.trim(req.getCertificateType());
        ServiceException.isTrue(StrUtil.isNotBlank(certificateTypeText), "证照类型不能为空");

        String queryCertificateType;
        String queryTemplateId = null;
        Optional<CertificateTypeEnum>  certificateTypeEnumOptional = CertificateTypeEnum.getByType(certificateTypeText);
        if(certificateTypeEnumOptional.isEmpty()){
            throw new ServiceException("证照类型错误!",400);
        }
        CertificateTypeEnum certificateTypeEnum = certificateTypeEnumOptional.get();
        queryCertificateType = certificateTypeEnum.getType();
        if(certificateTypeEnum.equals(CertificateTypeEnum.STORE_CUSTOM_CERTIFICATE)){
            queryTemplateId = certificateTypeText;
        }

        if (CertificateTypeEnum.HEALTH_CERTIFICATE.equals(certificateTypeEnum)) {
            return buildHealthCertificateDashboard(storeIdFilter, storeTypeFilter);
        }
        List<CertificateHealthDashboardStatusStatVO> statusStatList = baseMapper.queryStoreDashboardStatusStats(
                queryCertificateType, queryTemplateId, storeIdFilter, storeTypeFilter);
        if (CollectionUtils.isEmpty(statusStatList)) {
            return buildEmptyDashboard();
        }

        int totalMissingCount = 0;
        int totalNearExpireCount = 0;
        int totalExpiredCount = 0;
        int totalNormalCount = 0;
        Set<String> orgIds = new HashSet<>();
        Map<String,CertificateStoreCustomStatusTableVO> storeCustomStatusTableVOMap = new HashMap<>();
        for (CertificateHealthDashboardStatusStatVO row : statusStatList) {
            if (row == null) {
                continue;
            }
            String storeId = row.getStoreId();
            orgIds.add(storeId);
            CertificateStoreCustomStatusTableVO storeCustomStatusTableVO = storeCustomStatusTableVOMap.computeIfAbsent(storeId,v->new CertificateStoreCustomStatusTableVO());
            int status = row.getStatus() == null ? STATUS_MISSING : row.getStatus();
            int count = row.getTotalCount() == null ? 0 : row.getTotalCount();
            storeCustomStatusTableVO.addStatusCount(status, count);
            if (count <= 0) {
                continue;
            }
            if (status == STATUS_MISSING) {
                totalMissingCount += count;
                continue;
            }
            if (status == STATUS_NEAR_EXPIRE) {
                totalNearExpireCount += count;
                continue;
            }
            if (status == STATUS_EXPIRED) {
                totalExpiredCount += count;
                continue;
            }
            totalNormalCount += count;
        }

        Map<String, OrganizeGeneralDetailVO> organizeGeneralDetailVOMap = organizationHelper.buildOrganizeGenerals(orgIds);
        for (CertificateStoreCustomStatusTableVO certificateStoreCustomStatusTableVO : storeCustomStatusTableVOMap.values()){
            OrganizeGeneralDetailVO organizeGeneralDetailVO = organizeGeneralDetailVOMap.get(certificateStoreCustomStatusTableVO.getStoreId());
            if(Objects.nonNull(organizeGeneralDetailVO)){
                certificateStoreCustomStatusTableVO.setStoreName(organizeGeneralDetailVO.getName());
            }
            int totalCount = certificateStoreCustomStatusTableVO.getTotalCount();
            if(totalCount <= 0){
                continue;
            }
            certificateStoreCustomStatusTableVO.setMissingRatio(formatPercent(certificateStoreCustomStatusTableVO.getMissingCount(), totalCount));
            certificateStoreCustomStatusTableVO.setNearExpireRatio(formatPercent(certificateStoreCustomStatusTableVO.getNearExpireCount(), totalCount));
            certificateStoreCustomStatusTableVO.setExpiredRatio(formatPercent(certificateStoreCustomStatusTableVO.getExpiredCount(), totalCount));
            certificateStoreCustomStatusTableVO.setNormalRatio(formatPercent(certificateStoreCustomStatusTableVO.getNormalCount(), totalCount));
        }
        int totalCount = totalMissingCount + totalNearExpireCount + totalExpiredCount + totalNormalCount;

        CertificateStoreDashboardVO result = new CertificateStoreDashboardVO();
        result.setMissingCount(totalMissingCount);
        result.setNearExpireCount(totalNearExpireCount);
        result.setExpiredCount(totalExpiredCount);
        result.setNormalCount(totalNormalCount);
        result.setTotalCount(totalCount);
        result.setBarList(Collections.singletonList(new CertificateStoreCustomStatusBarVO("","",totalCount,totalMissingCount,totalNearExpireCount,totalExpiredCount,totalNormalCount)));

        List<CertificateStoreCustomStatusPieVO> pieList = new ArrayList<>();
        pieList.add(buildPieItem(BOARD_STATUS_MISSING, "缺失", totalMissingCount, totalCount));
        pieList.add(buildPieItem(BOARD_STATUS_NEAR_EXPIRE, "临期", totalNearExpireCount, totalCount));
        pieList.add(buildPieItem(BOARD_STATUS_EXPIRED, "过期", totalExpiredCount, totalCount));
        pieList.add(buildPieItem(BOARD_STATUS_NORMAL, "正常", totalNormalCount, totalCount));
        result.setPieList(pieList);
        return result;
    }

    @Override
    public PageInfo<CertificateStoreCustomStatusTableVO> storeCertificateDashboardTablePage(CertificateStoreDashboardReq req) {
        ServiceException.notNull(req, "请求参数不能为空");
        long current = req.getCurrentPage() == null || req.getCurrentPage() <= 0 ? 1L : req.getCurrentPage();
        long pageSize = req.getPageSize() == null || req.getPageSize() <= 0 ? 10L : req.getPageSize();

        List<String> storeIdFilter = req.getStoreIds();
        String storeTypeFilter = StrUtil.trim(req.getStoreType());
        String certificateTypeText = StrUtil.trim(req.getCertificateType());
        ServiceException.isTrue(StrUtil.isNotBlank(certificateTypeText), "证照类型不能为空");

        String queryCertificateType;
        String queryTemplateId = null;
        Optional<CertificateTypeEnum> certificateTypeEnumOptional = CertificateTypeEnum.getByType(certificateTypeText);
        if(certificateTypeEnumOptional.isEmpty()){
            throw new ServiceException("证照类型错误!",400);
        }
        CertificateTypeEnum certificateTypeEnum = certificateTypeEnumOptional.get();
        queryCertificateType = certificateTypeEnum.getType();
        if(certificateTypeEnum.equals(CertificateTypeEnum.STORE_CUSTOM_CERTIFICATE)){
            queryTemplateId = certificateTypeText;
        }

        if (CertificateTypeEnum.HEALTH_CERTIFICATE.equals(certificateTypeEnum)) {
            List<CertificateStoreCustomStatusTableVO> healthTableList = buildHealthCertificateDashboardTableList(storeIdFilter, storeTypeFilter);
            return pageByMemory(healthTableList, current, pageSize);
        }

        Page<CertificateStoreCustomStatusTableVO> page = new Page<>(current, pageSize);
        Page<CertificateStoreCustomStatusTableVO> queryPage = baseMapper.queryStoreDashboardTablePage(
                page,
                queryCertificateType,
                queryTemplateId,
                storeIdFilter,
                storeTypeFilter
        );
        List<CertificateStoreCustomStatusTableVO> records = queryPage.getRecords() == null ? Collections.emptyList() : queryPage.getRecords();
        Set<String> orgIds = new HashSet<>();
        for (CertificateStoreCustomStatusTableVO record : records) {
            orgIds.add(record.getStoreId());
            int totalCount = record.getTotalCount();
            if (totalCount <= 0) {
                continue;
            }
            record.setMissingRatio(formatPercent(record.getMissingCount(), totalCount));
            record.setNearExpireRatio(formatPercent(record.getNearExpireCount(), totalCount));
            record.setExpiredRatio(formatPercent(record.getExpiredCount(), totalCount));
            record.setNormalRatio(formatPercent(record.getNormalCount(), totalCount));
        }
        Map<String, OrganizeBaseInfoVO> organizeBaseInfoVOMap = organizationHelper.buildBaseOrganizeVO(orgIds,UserProvider.getUser().getTenantId());
        for (CertificateStoreCustomStatusTableVO record : records) {
            String orgId = record.getStoreId();
            OrganizeBaseInfoVO organizeBaseInfoVO = organizeBaseInfoVOMap.get(orgId);
            if(Objects.nonNull(organizeBaseInfoVO)){
                record.setStoreName(organizeBaseInfoVO.getName());
            }
        }

        PageInfo<CertificateStoreCustomStatusTableVO> pageInfo = new PageInfo<>();
        pageInfo.setList(records);
        pageInfo.setPageNum((int) queryPage.getCurrent());
        pageInfo.setPageSize((int) queryPage.getSize());
        pageInfo.setTotal((int) queryPage.getTotal());
        return pageInfo;
    }

    private CertificateStoreDashboardVO buildHealthCertificateDashboard(List<String> storeIdFilter,
                                                                        String storeTypeFilter) {
        List<CertificateHealthDashboardStatusStatVO> statusStatList = buildHealthCertificateStatusStat(storeIdFilter,storeTypeFilter);
        if(CollectionUtils.isEmpty(statusStatList)){
            return buildEmptyDashboard();
        }
        Map<String, HealthDashboardCounter> storeCounterMap = new LinkedHashMap<>();
        Set<String> orgIds = new HashSet<>();
        for (CertificateHealthDashboardStatusStatVO row : statusStatList) {
            if (row == null) {
                continue;
            }
            String storeId = StrUtil.trim(row.getStoreId());
            if (StrUtil.isBlank(storeId)) {
                continue;
            }
            String storeName = StrUtil.trim(row.getStoreName());
            if(StringUtil.isBlank(storeName)){
                orgIds.add(storeId);
            }
            HealthDashboardCounter counter = storeCounterMap.computeIfAbsent(storeId,v->new HealthDashboardCounter(storeId,storeName));
            int status = row.getStatus() == null ? 0 : row.getStatus();
            int count = row.getTotalCount() == null ? 0 : row.getTotalCount();
            counter.addStatusCount(status, count);
        }

        Map<String, OrganizeGeneralDetailVO> organizeGeneralDetailVOMap = organizationHelper.buildOrganizeGenerals(orgIds);

        List<CertificateStoreCustomStatusBarVO> barList = new ArrayList<>();
        List<CertificateStoreCustomStatusTableVO> tableList = new ArrayList<>();

        int totalMissingCount = 0;
        int totalNearExpireCount = 0;
        int totalExpiredCount = 0;
        int totalNormalCount = 0;
        int totalCount = 0;

        for (HealthDashboardCounter counter : storeCounterMap.values()) {
            String storeId = counter.getStoreId();
            String storeName = counter.getStoreName();
            if(StringUtil.isBlank(storeName)){
                OrganizeGeneralDetailVO organizeGeneralDetailVO = organizeGeneralDetailVOMap.get(storeId);
                if(Objects.nonNull(organizeGeneralDetailVO)){
                    counter.setStoreName(organizeGeneralDetailVO.getName());
                }
            }
            int storeTotalCount = counter.getTotalCount();
            int missingCount = counter.getMissingCount();
            int nearExpireCount = counter.getNearExpireCount();
            int expiredCount = counter.getExpiredCount();
            int normalCount = counter.getNormalCount();

            totalMissingCount += missingCount;
            totalNearExpireCount += nearExpireCount;
            totalExpiredCount += expiredCount;
            totalNormalCount += normalCount;
            totalCount += storeTotalCount;

            CertificateStoreCustomStatusBarVO barVO = new CertificateStoreCustomStatusBarVO();
            barVO.setStoreId(counter.getStoreId());
            barVO.setStoreName(counter.getStoreName());
            barVO.setTotalCount(storeTotalCount);
            barVO.setMissingCount(missingCount);
            barVO.setNearExpireCount(nearExpireCount);
            barVO.setExpiredCount(expiredCount);
            barVO.setNormalCount(normalCount);
            barList.add(barVO);

            CertificateStoreCustomStatusTableVO tableVO = new CertificateStoreCustomStatusTableVO();
            tableVO.setStoreId(counter.getStoreId());
            tableVO.setStoreName(counter.getStoreName());
            tableVO.setTotalCount(storeTotalCount);
            tableVO.setMissingCount(missingCount);
            tableVO.setMissingRatio(formatPercent(missingCount, storeTotalCount));
            tableVO.setNearExpireCount(nearExpireCount);
            tableVO.setNearExpireRatio(formatPercent(nearExpireCount, storeTotalCount));
            tableVO.setExpiredCount(expiredCount);
            tableVO.setExpiredRatio(formatPercent(expiredCount, storeTotalCount));
            tableVO.setNormalCount(normalCount);
            tableVO.setNormalRatio(formatPercent(normalCount, storeTotalCount));
            tableList.add(tableVO);
        }

        CertificateStoreDashboardVO result = new CertificateStoreDashboardVO();
        result.setMissingCount(totalMissingCount);
        result.setNearExpireCount(totalNearExpireCount);
        result.setExpiredCount(totalExpiredCount);
        result.setNormalCount(totalNormalCount);
        result.setTotalCount(totalCount);
        result.setBarList(barList);

        List<CertificateStoreCustomStatusPieVO> pieList = new ArrayList<>();
        pieList.add(buildPieItem(BOARD_STATUS_MISSING, "缺失", totalMissingCount, totalCount));
        pieList.add(buildPieItem(BOARD_STATUS_NEAR_EXPIRE, "临期", totalNearExpireCount, totalCount));
        pieList.add(buildPieItem(BOARD_STATUS_EXPIRED, "过期", totalExpiredCount, totalCount));
        pieList.add(buildPieItem(BOARD_STATUS_NORMAL, "正常", totalNormalCount, totalCount));
        result.setPieList(pieList);
        return result;
    }

    private List<CertificateHealthDashboardStatusStatVO> buildHealthCertificateStatusStat(List<String> storeIdFilter, String storeTypeFilter) {
        boolean isAdministrator = BooleanUtil.isTrue(UserProvider.getUser().getIsAdministrator());
        Map<String,List<String>> orgIdAndUserIds = null;
        if(isAdministrator){
            orgIdAndUserIds = organizationHelper.buildUserIdsByOrgIds(storeIdFilter,true,storeTypeFilter);
        }else{
            List<String> orgIds = permissionsUtils.obtainPersonnelOrganizationIdDataPermissions(UserProvider.getLoginUserId());
            orgIdAndUserIds = organizationHelper.buildUserIdsByOrgIds(buildQueryOrgIds(orgIds,storeIdFilter),false,storeTypeFilter);
        }

        List<HealthCertificateStatusDTO> healthCertificateStatusDTOList = baseMapper.queryHealthCertificateStatus(orgIdAndUserIds.values()
                .stream()
                .flatMap(Collection::stream)
                .collect(Collectors.toSet()));

        return buildHealthCertificateStatusStat(orgIdAndUserIds,healthCertificateStatusDTOList);
    }

    private List<CertificateHealthDashboardStatusStatVO> buildHealthCertificateStatusStat(Map<String, List<String>> orgIdAndUserIds,
                                                                                          List<HealthCertificateStatusDTO> healthCertificateStatusDTOList) {
        if(CollectionUtils.isEmpty(healthCertificateStatusDTOList)){
            return Collections.emptyList();
        }

        Map<String,HealthCertificateStatusDTO> userIdAndStatusDTOMap = healthCertificateStatusDTOList.stream()
                .collect(Collectors.toMap(HealthCertificateStatusDTO::getUserId,v->v));
        Map<String,Map<Integer,Integer>> orgIdAndStatusCountMap = new HashMap<>();
        for (Map.Entry<String, List<String>> entry:orgIdAndUserIds.entrySet()){
            String orgId = entry.getKey();
            List<String> userIds = entry.getValue();
            Map<Integer,Integer> statusCountMap = orgIdAndStatusCountMap.computeIfAbsent(orgId,v->new HashMap<>());
            for (String userId:userIds){
                HealthCertificateStatusDTO statusDTO = userIdAndStatusDTOMap.get(userId);
                if(Objects.isNull(statusDTO)){
                    continue;
                }
                int status = statusDTO.getStatus();
                int count = statusCountMap.getOrDefault(status,0);
                count++;
                statusCountMap.put(status,count);
            }
        }
        List<CertificateHealthDashboardStatusStatVO> result = new ArrayList<>();
        for (Map.Entry<String, Map<Integer, Integer>> entry:orgIdAndStatusCountMap.entrySet()){
            String orgId = entry.getKey();
            Map<Integer, Integer> statusCountMap = entry.getValue();
            for (Map.Entry<Integer, Integer> entry1:statusCountMap.entrySet()){
                Integer status = entry1.getKey();
                Integer count = entry1.getValue();
                CertificateHealthDashboardStatusStatVO statusStatVO = new CertificateHealthDashboardStatusStatVO();
                statusStatVO.setStoreId(orgId);
                statusStatVO.setStatus(status);
                statusStatVO.setTotalCount(count);
                result.add(statusStatVO);
            }
        }
        return result;
    }

    private Collection<String> buildQueryOrgIds(List<String> hasPermissionOrgIds, List<String> queryOrgIds) {
        if(CollectionUtils.isEmpty(hasPermissionOrgIds)){
            return Collections.emptyList();
        }
        if(CollectionUtils.isEmpty(queryOrgIds)){
            return hasPermissionOrgIds;
        }
        Set<String> hasPermissionOrgIdsSet = new HashSet<>(hasPermissionOrgIds);
        return queryOrgIds.stream()
                .filter(hasPermissionOrgIdsSet::contains)
                .collect(Collectors.toSet());
    }

    private static class HealthDashboardCounter {
        private final String storeId;
        private String storeName;
        private int missingCount;
        private int nearExpireCount;
        private int expiredCount;
        private int normalCount;
        private int totalCount;

        private HealthDashboardCounter(String storeId, String storeName) {
            this.storeId = storeId;
            this.storeName = storeName;
        }

        private void addStatusCount(int status, int count) {
            if (count <= 0) {
                return;
            }
            this.totalCount += count;
            if (status == STATUS_MISSING) {
                this.missingCount += count;
                return;
            }
            if (status == STATUS_NEAR_EXPIRE) {
                this.nearExpireCount += count;
                return;
            }
            if (status == STATUS_EXPIRED) {
                this.expiredCount += count;
                return;
            }
            if (status == STATUS_NORMAL) {
                this.normalCount += count;
            }
        }

        private String getStoreId() {
            return storeId;
        }

        private String getStoreName() {
            return storeName;
        }

        private void setStoreName(String storeName) {
            this.storeName = storeName;
        }

        private int getMissingCount() {
            return missingCount;
        }

        private int getNearExpireCount() {
            return nearExpireCount;
        }

        private int getExpiredCount() {
            return expiredCount;
        }

        private int getNormalCount() {
            return normalCount;
        }

        private int getTotalCount() {
            return totalCount;
        }
    }

    private String normalizeStoreCertificateType(String certificateType) {
        String type = StrUtil.blankToDefault(StrUtil.trim(certificateType), CertificateTypeEnum.BUSINESS_LICENSE.getType());
        boolean supported = StrUtil.equalsIgnoreCase(type, CertificateTypeEnum.BUSINESS_LICENSE.getType())
                || StrUtil.equalsIgnoreCase(type, CertificateTypeEnum.HYGIENE_LICENSE.getType());
        ServiceException.isTrue(supported, "门店证照类型只支持营业执照或食品许可");
        return StrUtil.equalsIgnoreCase(type, CertificateTypeEnum.HYGIENE_LICENSE.getType())
                ? CertificateTypeEnum.HYGIENE_LICENSE.getType()
                : CertificateTypeEnum.BUSINESS_LICENSE.getType();
    }

    private Integer calculateStoreCertificateStatus(CertificateInstanceEntity instanceEntity) {
        if (instanceEntity == null) {
            return BOARD_STATUS_MISSING;
        }
        return instanceEntity.getStatus();
    }

    private String resolveStoreCertificateStatusName(Integer status) {
        if (Integer.valueOf(BOARD_STATUS_MISSING).equals(status)) {
            return "缺失";
        }
        if (Integer.valueOf(BOARD_STATUS_NEAR_EXPIRE).equals(status)) {
            return "临期";
        }
        if (Integer.valueOf(BOARD_STATUS_EXPIRED).equals(status)) {
            return "过期";
        }
        return "正常";
    }

    private String resolveOrganizeCategoryName(OrganizeCategoryEnums categoryEnums) {
        if (OrganizeCategoryEnums.COMPANY.equals(categoryEnums)) {
            return "公司";
        }
        if (OrganizeCategoryEnums.STORE.equals(categoryEnums)) {
            return "门店";
        }
        if (OrganizeCategoryEnums.DEPARTMENT.equals(categoryEnums)) {
            return "部门";
        }
        if (OrganizeCategoryEnums.TEAM.equals(categoryEnums)) {
            return "班组";
        }
        return "-";
    }

    private String resolveParentOrgName(OrganizeGeneralDetailVO store, Map<String, String> orgNameMap) {
        if (store == null) {
            return "-";
        }
        String parentName = orgNameMap.get(StrUtil.trim(store.getParentId()));
        if (StrUtil.isNotBlank(parentName)) {
            return parentName;
        }
        List<String> treeNodes = splitOrganizeTreeName(store.getOrganizeTreeName());
        if (treeNodes.size() >= 2) {
            return treeNodes.get(treeNodes.size() - 2);
        }
        return "-";
    }

    private String resolveStoreTypeName(OrganizeGeneralDetailVO store, Map<String, String> orgNameMap) {
        if (store == null || !OrganizeCategoryEnums.STORE.equals(store.getOrganizeCategoryEnums())) {
            return "-";
        }
        return StoreTypeUtils.buildStoreType(store.getStoreTag());
    }

    private List<String> splitOrganizeTreeName(String organizeTreeName) {
        if (StrUtil.isBlank(organizeTreeName)) {
            return Collections.emptyList();
        }
        String normalized = organizeTreeName
                .replace("\\", "/")
                .replace(">", "/")
                .replace("／", "/");
        return StrUtil.split(normalized, '/')
                .stream()
                .filter(StrUtil::isNotBlank)
                .map(StrUtil::trim)
                .collect(Collectors.toList());
    }

    private String formatBusinessTerm(CertificateInstanceEntity entity) {
        if (entity == null) {
            return "-";
        }
        if (Integer.valueOf(1).equals(entity.getIsLongTerm()) || entity.getExpireDate() == null) {
            return "长期";
        }
        String issueText = entity.getIssueDate() == null ? "-" : DateUtil.formatDate(entity.getIssueDate());
        String expireText = DateUtil.formatDate(entity.getExpireDate());
        return issueText + " ~ " + expireText;
    }

    private List<OrganizeGeneralDetailVO> queryPermissionStores(Set<String> storeIdFilter, String storeTypeFilter) {
        List<OrganizeGeneralDetailVO> storeList = ftbPermissionOrganizeService.authOrganizesByUserBound(
                List.of(OrganizeCategoryEnums.STORE,OrganizeCategoryEnums.COMPANY), null, false, false);
        if (CollUtil.isEmpty(storeList)) {
            return Collections.emptyList();
        }
        Map<String, OrganizeGeneralDetailVO> targetStoreMap = new LinkedHashMap<>();
        for (OrganizeGeneralDetailVO store : storeList) {
            //被禁用了过滤下
            if(BooleanUtil.isFalse(store.getEnabled())){
                continue;
            }
            if (store == null || StrUtil.isBlank(store.getId())) {
                continue;
            }
            String storeId = StrUtil.trim(store.getId());
            if (CollectionUtils.isNotEmpty(storeIdFilter) && !storeIdFilter.contains(storeId)) {
                continue;
            }
            String storeType = StrUtil.trim(store.getStoreTag());
            if (StrUtil.isNotBlank(storeTypeFilter) && !StrUtil.equals(storeType, storeTypeFilter)) {
                continue;
            }
            //覆盖后者
            targetStoreMap.put(storeId, store);
        }
        return new ArrayList<>(targetStoreMap.values());
    }

    private CertificateInstanceEntity queryActiveEntity(String id) {
        CertificateInstanceEntity entity = baseMapper.selectById(id);
        ServiceException.notNull(entity, "证照实例不存在");
        ServiceException.isTrue(Integer.valueOf(0).equals(entity.getEnabledMark()), "证照实例不存在");
        return entity;
    }


    private void validateSaveReq(CertificateInstanceAddReq req) {
        ServiceException.notNull(req.getType(), "证照类型不能为空");
        if (CertificateTypeEnum.STORE_CUSTOM_CERTIFICATE.equals(req.getType())) {
            ServiceException.isTrue(StrUtil.isNotBlank(req.getTemplateId()), "门店自定义证照必须传模板ID");
        }
        if (req.getIsLongTerm() != null && req.getIsLongTerm() == 0) {
            ServiceException.isTrue(StrUtil.isNotBlank(req.getExpireDate()), "非长期证照必须传到期日期");
        }
        if (req.getItemList() == null) {
            req.setItemList(new ArrayList<>());
        }
        for (CertificateInstanceItemReq itemReq : req.getItemList()) {
            ServiceException.notNull(itemReq, "证照明细不能为空");
            ServiceException.isTrue(StrUtil.isNotBlank(itemReq.getItemName()), "证照明细名称不能为空");
        }
    }


    private void normalizeSaveEntity(CertificateInstanceEntity entity) {
        entity.setCertificateType(StrUtil.trim(entity.getCertificateType()));
        entity.setTemplateId(StrUtil.trim(entity.getTemplateId()));
        entity.setSubjectId(StrUtil.trim(entity.getSubjectId()));
        entity.setCertificateNo(StrUtil.trim(entity.getCertificateNo()));
        entity.setCertificateImage(StrUtil.trim(entity.getCertificateImage()));
    }


    private void saveItems(String instanceId, List<CertificateInstanceItemReq> itemList) {
        List<CertificateInstanceItemReq> safeList = itemList == null ? Collections.emptyList() : itemList;
        long defaultSort = 1L;
        for (CertificateInstanceItemReq itemReq : safeList) {
            if (itemReq == null || StrUtil.isBlank(itemReq.getItemName())) {
                continue;
            }
            CertificateInstanceItemEntity itemEntity = new CertificateInstanceItemEntity();
            itemEntity.setId(FtbUtil.getId());
            itemEntity.setInstanceId(instanceId);
            itemEntity.setTemplateItemId(StrUtil.trim(itemReq.getTemplateItemId()));
            itemEntity.setItemName(StrUtil.trim(itemReq.getItemName()));
            itemEntity.setItemType(itemReq.getItemType());
            itemEntity.setItemValue(StrUtil.trim(itemReq.getItemValue()));
            itemEntity.setSorts(itemReq.getSorts() == null ? defaultSort : itemReq.getSorts());
            itemEntity.setEnabledMark(0);
            certificateInstanceItemMapper.insert(itemEntity);
            defaultSort++;
        }
    }


    private Date parseDate(String dateText, String fieldName) {
        if (StrUtil.isBlank(dateText)) {
            return null;
        }
        try {
            return DateUtil.parseDate(StrUtil.trim(dateText));
        } catch (Exception e) {
            throw new ServiceException(fieldName + "格式不正确，正确格式为yyyy-MM-dd");
        }
    }


    private Integer calculateStatus(Integer isLongTerm, Date expireDate) {
        return calculateStatus(isLongTerm, expireDate, NEAR_EXPIRE_DAYS);
    }


    private Integer calculateStatus(Integer isLongTerm, Date expireDate, Integer nearExpireDays) {
        if (isLongTerm != null && isLongTerm == 1) {
            return STATUS_NORMAL;
        }
        if (expireDate == null) {
            return STATUS_MISSING;
        }
        Date today = DateUtil.beginOfDay(DateUtil.date());
        Date target = DateUtil.beginOfDay(expireDate);
        if (target.before(today)) {
            return STATUS_EXPIRED;
        }
        int limitDays = nearExpireDays == null || nearExpireDays < 0 ? 0 : nearExpireDays;
        long daysDiff = DateUtil.betweenDay(today, target, false);
        if (daysDiff <= limitDays) {
            return STATUS_NEAR_EXPIRE;
        }
        return STATUS_NORMAL;
    }


    private Map<String, CertificateInstanceEntity> queryCertificateInstanceMapBySubject(List<String> subjectIds, Integer subjectType,
                                                                                        String certificateType, String templateId) {
        if (CollUtil.isEmpty(subjectIds) || subjectType == null || StrUtil.isBlank(certificateType)) {
            return Collections.emptyMap();
        }

        LambdaQueryWrapper<CertificateInstanceEntity> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(CertificateInstanceEntity::getEnabledMark, 0);
        queryWrapper.eq(CertificateInstanceEntity::getCertificateType, StrUtil.trim(certificateType));
        queryWrapper.eq(CertificateInstanceEntity::getSubjectType, subjectType);
        queryWrapper.in(CertificateInstanceEntity::getSubjectId, subjectIds);
        if (StrUtil.isNotBlank(templateId)) {
            queryWrapper.eq(CertificateInstanceEntity::getTemplateId, StrUtil.trim(templateId));
        }
        queryWrapper.orderByDesc(CertificateInstanceEntity::getLastModifyTime);
        queryWrapper.orderByDesc(CertificateInstanceEntity::getCreatorTime);

        List<CertificateInstanceEntity> instanceList = baseMapper.selectList(queryWrapper);
        if (CollUtil.isEmpty(instanceList)) {
            return Collections.emptyMap();
        }

        Map<String, CertificateInstanceEntity> result = new HashMap<>();
        for (CertificateInstanceEntity entity : instanceList) {
            if (entity == null || StrUtil.isBlank(entity.getSubjectId())) {
                continue;
            }
            result.putIfAbsent(StrUtil.trim(entity.getSubjectId()), entity);
        }
        return result;
    }


    private int queryStoreCustomTemplateReminderDays(String templateId) {
        if (StrUtil.isBlank(templateId)) {
            return NEAR_EXPIRE_DAYS;
        }

        LambdaQueryWrapper<StoreCertificatePhotoEntity> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(StoreCertificatePhotoEntity::getEnabledMark, 0);
        queryWrapper.eq(StoreCertificatePhotoEntity::getId, StrUtil.trim(templateId));

        StoreCertificatePhotoEntity templateEntity = storeCertificatePhotoMapper.selectOne(queryWrapper);
        if (templateEntity == null || templateEntity.getExpiryReminderDays() == null || templateEntity.getExpiryReminderDays() < 0) {
            return NEAR_EXPIRE_DAYS;
        }
        return templateEntity.getExpiryReminderDays();
    }


    private CertificateStoreCustomStatusPieVO buildPieItem(Integer status, String statusName, Integer count, Integer total) {
        CertificateStoreCustomStatusPieVO pieVO = new CertificateStoreCustomStatusPieVO();
        pieVO.setStatus(status);
        pieVO.setStatusName(statusName);
        pieVO.setCount(count == null ? 0 : count);
        pieVO.setRatio(formatPercent(pieVO.getCount(), total == null ? 0 : total));
        return pieVO;
    }


    private BigDecimal formatPercent(int part, int total) {
        if (part <= 0 || total <= 0) {
            return BigDecimal.ZERO;
        }
        return BigDecimal.valueOf(part)
                .multiply(BigDecimal.valueOf(100))
                .divide(BigDecimal.valueOf(total), 2, RoundingMode.HALF_UP);
    }


    private CertificateStoreDashboardVO buildEmptyDashboard() {
        CertificateStoreDashboardVO result = new CertificateStoreDashboardVO();
        List<CertificateStoreCustomStatusPieVO> pieList = new ArrayList<>();
        pieList.add(buildPieItem(BOARD_STATUS_MISSING, "缺失", 0, 0));
        pieList.add(buildPieItem(BOARD_STATUS_NEAR_EXPIRE, "临期", 0, 0));
        pieList.add(buildPieItem(BOARD_STATUS_EXPIRED, "过期", 0, 0));
        pieList.add(buildPieItem(BOARD_STATUS_NORMAL, "正常", 0, 0));
        result.setPieList(pieList);
        return result;
    }


    private List<CertificateStoreCustomStatusTableVO> buildHealthCertificateDashboardTableList(List<String> storeIdFilter,
                                                                                                String storeTypeFilter) {
        List<CertificateHealthDashboardStatusStatVO> statusStatList = buildHealthCertificateStatusStat(storeIdFilter,storeTypeFilter);
        if (CollectionUtils.isEmpty(statusStatList)) {
            return Collections.emptyList();
        }

        Map<String, HealthDashboardCounter> storeCounterMap = new LinkedHashMap<>();
        Set<String> orgIds = new HashSet<>();
        for (CertificateHealthDashboardStatusStatVO row : statusStatList) {
            if (row == null) {
                continue;
            }
            String storeId = StrUtil.trim(row.getStoreId());
            if (StrUtil.isBlank(storeId)) {
                continue;
            }
            String storeName = StrUtil.trim(row.getStoreName());
            if (StringUtil.isBlank(storeName)) {
                orgIds.add(storeId);
            }
            HealthDashboardCounter counter = storeCounterMap.computeIfAbsent(storeId, v -> new HealthDashboardCounter(storeId, storeName));
            int status = row.getStatus() == null ? 0 : row.getStatus();
            int count = row.getTotalCount() == null ? 0 : row.getTotalCount();
            counter.addStatusCount(status, count);
        }

        Map<String, OrganizeGeneralDetailVO> organizeGeneralDetailVOMap = organizationHelper.buildOrganizeGenerals(orgIds);
        List<CertificateStoreCustomStatusTableVO> tableList = new ArrayList<>();
        for (HealthDashboardCounter counter : storeCounterMap.values()) {
            String storeId = counter.getStoreId();
            String storeName = counter.getStoreName();
            if (StringUtil.isBlank(storeName)) {
                OrganizeGeneralDetailVO organizeGeneralDetailVO = organizeGeneralDetailVOMap.get(storeId);
                if (Objects.nonNull(organizeGeneralDetailVO)) {
                    storeName = organizeGeneralDetailVO.getName();
                }
            }

            int storeTotalCount = counter.getTotalCount();
            int missingCount = counter.getMissingCount();
            int nearExpireCount = counter.getNearExpireCount();
            int expiredCount = counter.getExpiredCount();
            int normalCount = counter.getNormalCount();

            CertificateStoreCustomStatusTableVO tableVO = new CertificateStoreCustomStatusTableVO();
            tableVO.setStoreId(storeId);
            tableVO.setStoreName(storeName);
            tableVO.setTotalCount(storeTotalCount);
            tableVO.setMissingCount(missingCount);
            tableVO.setMissingRatio(formatPercent(missingCount, storeTotalCount));
            tableVO.setNearExpireCount(nearExpireCount);
            tableVO.setNearExpireRatio(formatPercent(nearExpireCount, storeTotalCount));
            tableVO.setExpiredCount(expiredCount);
            tableVO.setExpiredRatio(formatPercent(expiredCount, storeTotalCount));
            tableVO.setNormalCount(normalCount);
            tableVO.setNormalRatio(formatPercent(normalCount, storeTotalCount));
            tableList.add(tableVO);
        }
        tableList.sort(Comparator.comparing(CertificateStoreCustomStatusTableVO::getStoreName, Comparator.nullsLast(String::compareTo)));
        return tableList;
    }

    private <T> PageInfo<T> pageByMemory(List<T> allData, long current, long pageSize) {
        List<T> safeData = allData == null ? Collections.emptyList() : allData;
        int total = safeData.size();
        int fromIndex = (int) ((current - 1) * pageSize);
        int toIndex = (int) Math.min(fromIndex + pageSize, total);
        List<T> pageList;
        if (fromIndex >= total || fromIndex < 0) {
            pageList = Collections.emptyList();
        } else {
            pageList = new ArrayList<>(safeData.subList(fromIndex, toIndex));
        }
        PageInfo<T> pageInfo = new PageInfo<>();
        pageInfo.setList(pageList);
        pageInfo.setPageNum((int) current);
        pageInfo.setPageSize((int) pageSize);
        pageInfo.setTotal(total);
        return pageInfo;
    }

}
