StoreServiceImpl.java 17.8 KB
package vion.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.github.yulichang.base.MPJBaseServiceImpl;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import io.github.linpeilie.Converter;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import vion.dto.StatusDTO;
import vion.dto.StoreDTO;
import vion.mapper.StoreMapper;
import vion.mapper.TaskMapper;
import vion.model.*;
import vion.service.*;
import vion.vo.ContractVO;
import vion.vo.StoreVO;
import vion.vo.TagVO;
import vion.vo.UserVO;

import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class StoreServiceImpl extends MPJBaseServiceImpl<StoreMapper, Store> implements IStoreService {

    private final IAccountService accountService;
    private final IRContractStoreService contractStoreService;
    private final IContractPaymentService contractPaymentService;
    private final IContractService contractService;
    private final IFileService fileService;
    private final IFaultLogService faultLogService;
    private final IFormService formService;
    private final IInspectService inspectService;
    private final ITagService tagService;
    private final IRStoreTagService storeTagService;
    private final Converter converter;
    // 引入 taskService 会循环依赖
    private final TaskMapper taskMapper;

    @Value("${fileUrl:}")
    private String fileUrl;

    @Override
    public Page<StoreVO> getStoreList(StoreDTO data) {
        Store store = converter.convert(data, Store.class);
        Page<Store> storeList = this.lambdaQuery(store)
                .page(Page.of(data.getPageNum(), data.getPageSize()));
        // todo 缓存
        List<Account> accountList = accountService.list();
        List<FileInfo> fileInfoList = fileService.lambdaQuery().eq(FileInfo::getSourceType, 1).list();
        Map<Long, Long> store2CntMap = fileInfoList.stream().collect(Collectors.groupingBy(FileInfo::getStoreId, Collectors.counting()));

        List<Long> storeIdList = Opt.ofEmptyAble(storeList.getRecords())
                .map(recs -> recs.stream().map(Store::getId).collect(Collectors.toList()))
                .orElse(ListUtil.empty());
        // storeId -> 项目绑定的合同的 list
        Map<Long, List<RContractStore>> storeIdMap = Opt.ofEmptyAble(storeIdList)
                .map(l -> contractStoreService.lambdaQuery().in(RContractStore::getStoreId, l).list())
                .map(contractStoreList -> contractStoreList.stream().collect(Collectors.groupingBy(RContractStore::getStoreId)))
                .orElse(MapUtil.empty());
        // 项目绑定的合同的id
        List<Long> contractIdList = Opt.ofEmptyAble(storeIdMap.values())
                .map(l -> l.stream().flatMap(List::stream).collect(Collectors.toList()))
                .filter(CollUtil::isNotEmpty)
                .map(contractStoreList -> contractStoreList.stream().map(RContractStore::getContractId).collect(Collectors.toList())).
                orElse(ListUtil.empty());
        // contractId -> contract
        Map<Long, Contract> id2ContractMap = Opt.ofEmptyAble(contractIdList)
                .map(l -> contractService.lambdaQuery()
                        .select(Contract::getId, Contract::getName, Contract::getContractNo, Contract::getType, Contract::getSignDate, Contract::getWarrantyPeriod, Contract::getFinalDate, Contract::getStatus, Contract::getSaleName, Contract::getCustomerName, Contract::getMaintainSdate, Contract::getMaintainEdate)
                        .in(Contract::getId, l).list())
                .map(contractList -> contractList.stream().collect(Collectors.toMap(Contract::getId, Function.identity())))
                .orElse(MapUtil.empty());

        // contractId -> 合同付款约定
        Map<Long, List<ContractPayment>> contractId2CpMap = Opt.ofEmptyAble(contractIdList)
                .map(l -> contractPaymentService.lambdaQuery().in(ContractPayment::getContractId, l).in(ContractPayment::getPaymentType, 3, 4).isNotNull(ContractPayment::getNodeDate).list())
                .map(contractPaymentList -> contractPaymentList.stream().collect(Collectors.groupingBy(ContractPayment::getContractId, Collectors.collectingAndThen(Collectors.toList(), v -> {
                    v.sort(Comparator.comparing(ContractPayment::getPaymentType).reversed());
                    return v;
                }))))
                .orElse(MapUtil.empty());

        Map<Long, List<Long>> store2TaskIdMap = Opt.ofEmptyAble(storeIdList)
                .map(l -> taskMapper.selectList(Wrappers.lambdaQuery(Task.class).in(Task::getStoreId, l)))
                .map(taskList -> taskList.stream().collect(Collectors.groupingBy(Task::getStoreId, Collectors.mapping(Task::getId, Collectors.toList()))))
                .orElse(MapUtil.empty());
        List<Long> taskIds = store2TaskIdMap.values().stream()
                .flatMap(List::stream)
                .collect(Collectors.toList());
        Map<Long, List<ServiceOrder>> taskServiceOrderMap = Opt.ofEmptyAble(taskIds)
                .map(t -> Db.list(Wrappers.lambdaQuery(ServiceOrder.class).in(ServiceOrder::getTaskId, taskIds)))
                .map(serviceOrderList -> serviceOrderList.stream().collect(Collectors.groupingBy(ServiceOrder::getTaskId)))
                .orElse(MapUtil.empty());

        // storeId -> tagList
        Map<Long, List<TagVO>> storeId2TagMap = Opt.ofEmptyAble(storeIdList)
                .map(l -> {
                    MPJLambdaWrapper<Tag> wrapper = new MPJLambdaWrapper<Tag>()
                            .selectAll(Tag.class)
                            .leftJoin(RStoreTag.class, RStoreTag::getTagId, Tag::getId)
                            .in(RStoreTag::getStoreId, l);
                    return tagService.selectJoinList(TagVO.class, wrapper);
                })
                .filter(CollUtil::isNotEmpty)
                .map(tagVOS -> tagVOS.stream().collect(Collectors.groupingBy(TagVO::getStoreId)))
                .orElse(MapUtil.empty());

        List<StoreVO> storeVOList = new ArrayList<>();
        storeList.getRecords().forEach(item -> {
            StoreVO storeVO = converter.convert(item, StoreVO.class);
            storeVO.setAccountName(accountList.stream().filter(v -> v.getId().equals(item.getAccountId())).map(Account::getName).findFirst().orElse("--"));
            storeVO.setFileNum(store2CntMap.getOrDefault(item.getId(), 0L));

            Long storeId = item.getId();
            // 关联合同
            List<RContractStore> contractStores = storeIdMap.get(storeId);
            storeVO.setContractCount(Opt.ofEmptyAble(contractStores).map(List::size).orElse(0));
            storeVO.setContractVOList(id2ContractMap.values().stream()
                    .filter(v -> CollUtil.isNotEmpty(contractStores) && contractStores.stream().map(RContractStore::getContractId).collect(Collectors.toList()).contains(v.getId()))
                    .map(v -> converter.convert(v, ContractVO.class))
                    .collect(Collectors.toList()));

            Contract masterContract = id2ContractMap.getOrDefault(item.getMasterContract(), null);
            // 关联合同验收时间
            Opt.ofNullable(masterContract)
                    .map(c -> contractId2CpMap.getOrDefault(c.getId(), ListUtil.empty()))
                    .filter(CollUtil::isNotEmpty)
                    .map(l -> l.get(0).getNodeDate())
                    .ifPresent(nodeDate -> masterContract.setFinalDate(nodeDate));
            storeVO.setMainContract(Opt.ofNullable(masterContract).map(c -> converter.convert(c, ContractVO.class)).orElse(null));

            storeVO.setTaskCount(store2TaskIdMap.getOrDefault(storeId, ListUtil.empty()).size());

            // 补充服务单
            if (store2TaskIdMap.containsKey(item.getId())) {
                List<ServiceOrder> serviceOrders = store2TaskIdMap.get(item.getId())
                        .stream().flatMap(taskId -> taskServiceOrderMap.getOrDefault(taskId, ListUtil.empty()).stream())
                        .collect(Collectors.toList());
                storeVO.setServiceOrderCount(serviceOrders.size());
            }
            // 关联标签
            storeVO.setTagList(storeId2TagMap.getOrDefault(storeId, ListUtil.empty()));
            storeVOList.add(storeVO);
        });
        return Page.<StoreVO>of(data.getPageNum(), data.getPageSize(), storeList.getTotal()).setRecords(storeVOList);
    }

    @Override
    public String updateStoreStage(StatusDTO statusDTO, String token) {
        this.update(Wrappers.<Store>lambdaUpdate()
                .set(Store::getProjectStage, statusDTO.getProjectStage())
                .eq(Store::getId, statusDTO.getStoreId()));
        UserVO userVO = (UserVO) StpUtil.getTokenSession().get("curLoginUser");

        Opt.ofNullable(statusDTO.getFiles()).ifPresent(tmpFiles -> {
            for (MultipartFile infile : tmpFiles) {
                //上传url地址
                String orgName = infile.getOriginalFilename();
                String mainName = FileUtil.mainName(orgName);
                String fileExt = FileUtil.extName(orgName);
                String filename = StrUtil.format("{}_{}.{}", mainName, DateUtil.format(new Date(), "yyyyMMdd_HHmmssSSS"), fileExt);
                String path = fileUrl + FileUtil.FILE_SEPARATOR + statusDTO.getStoreId() + FileUtil.FILE_SEPARATOR + statusDTO.getSourceId() + FileUtil.FILE_SEPARATOR + filename;
                File file = FileUtil.touch(path);
                try {
                    infile.transferTo(file);
                } catch (IOException e) {
                    log.error("保存文件出错", e);
                }
                // storeId = sourceId
                FileInfo tempFileInfo = new FileInfo();
                tempFileInfo.setStoreId(statusDTO.getStoreId());
                tempFileInfo.setSourceId(statusDTO.getSourceId());
                tempFileInfo.setSourceType(statusDTO.getSourceType());
                tempFileInfo.setName(filename);
                tempFileInfo.setUrl(path);
                tempFileInfo.setType(FileUtil.extName(file));
                tempFileInfo.setSha256(SecureUtil.sha256(file).toUpperCase());
                tempFileInfo.setUploader(userVO.getUsername());

                fileService.save(tempFileInfo);
            }
        });
        return "更新成功";
    }

    @Override
    public String calMaintainStatus(List<Long> storeIdList) {
        List<Store> storeList = this.lambdaQuery().in(CollUtil.isNotEmpty(storeIdList), Store::getId, storeIdList).list();
        Map<Long, List<Long>> storeId2ContractIdMap = Opt.ofEmptyAble(contractStoreService.list())
                .map(l -> l.stream().collect(Collectors.groupingBy(RContractStore::getStoreId, Collectors.mapping(RContractStore::getContractId, Collectors.toList()))))
                .orElse(MapUtil.empty());
        List<Contract> contractList = contractService.listByIds(storeId2ContractIdMap.values().stream().flatMap(List::stream).collect(Collectors.toList()));
        // 根据 storeList、storeId2ContractIdMap、contractList,得到一个storeId 为 key,List<Contract> 为 value 的 map
        Map<Long, List<Contract>> storeId2ContractListMap = storeList.stream()
                .collect(Collectors.toMap(Store::getId, store ->
                        storeId2ContractIdMap.getOrDefault(store.getId(), ListUtil.empty())
                                .stream()
                                .map(contractId -> contractList.stream().filter(contract -> contract.getId().equals(contractId)).findFirst().orElse(null))
                                .filter(Objects::nonNull)
                                .collect(Collectors.toList())));

        List<Store> updStoreList = new ArrayList<>();
        for (Store s : storeList) {
            s.setMaintainStatus("--");
            List<Contract> contracts = storeId2ContractListMap.getOrDefault(s.getId(), ListUtil.empty());
            if (CollUtil.isNotEmpty(contracts)) {
                // 1.项目关联的主合同在维保期内,那么这个项目就是在保
                Contract masterContract = contracts.stream().filter(contract -> ObjUtil.equals(contract.getId(), s.getMasterContract())).findFirst().orElse(null);
                if (ObjUtil.isNotNull(masterContract)) {
                    Date finalDate = masterContract.getFinalDate();
                    Integer warrantyPeriod = masterContract.getWarrantyPeriod();
                    if (ArrayUtil.isAllNotNull(finalDate, warrantyPeriod)) {
                        Date finalDateEnd = DateUtil.offsetMonth(finalDate, warrantyPeriod);
                        boolean isIn = DateUtil.isIn(new Date(), finalDate, finalDateEnd);
                        if (isIn) {
                            s.setMaintainStatus("在保");
                            continue;
                        } else {
                            s.setMaintainStatus("脱保");
                        }
                    }
                }

                // 2.主合同脱保,那就判断维保合同
                Set<Boolean> maintainStatusSet = new HashSet<>();
                contracts.stream()
                        .filter(contract -> ObjUtil.equals(contract.getType(), 1))
                        .forEach(c -> {
                            Date maintainEdate = c.getMaintainEdate();
                            if (ObjUtil.isNotNull(maintainEdate)) {
                                // 维保结束时间大于等于当前时间,返回 true
                                maintainStatusSet.add(DateUtil.compare(maintainEdate, new Date()) >= 0);
                            }
                        });
                s.setMaintainStatus(CollUtil.isEmpty(maintainStatusSet) ? s.getMaintainStatus() : maintainStatusSet.contains(true) ? "在保" : "脱保");
            }
            Store updStore = new Store();
            updStore.setId(s.getId());
            updStore.setMaintainStatus(s.getMaintainStatus());
            updStoreList.add(updStore);
        }
        this.updateBatchById(updStoreList);
        return "计算成功";
    }

    @Override
    public List<StoreVO> storeScreen() {
        MPJLambdaWrapper<Store> wrapper = new MPJLambdaWrapper<Store>()
                .select(Store::getId, Store::getName, Store::getTaskDetail, Store::getFinishDate, Store::getPercentage, Store::getStuckPoint, Store::getIsImportant)
                .selectCollection(User.class, StoreVO::getUserNameList, map -> map.result(User::getUsername))
                .leftJoin(RStoreUser.class, on -> on.eq(RStoreUser::getStoreId, Store::getId).eq(RStoreUser::getIsMain, 1))
                .leftJoin(User.class, User::getId, RStoreUser::getUserId)
                .eq(Store::getIsImportant, 1)
                .orderByAsc(Store::getFinishDate);
        return this.selectJoinList(StoreVO.class, wrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String mergeStore(StoreDTO dto) {
        // targetId 合并到 sourceId
        Long sourceId = dto.getSourceId();
        Store store = this.getById(sourceId);
        Long targetId = dto.getTargetId();
        // 项目关联合同合并
        Opt.ofEmptyAble(contractStoreService.lambdaQuery().select(RContractStore::getContractId).eq(RContractStore::getStoreId, targetId).list())
                .ifPresent(l -> {
                    List<RContractStore> existList = contractStoreService.lambdaQuery().select(RContractStore::getContractId).eq(RContractStore::getStoreId, sourceId).list();

                    List<RContractStore> insList = l.stream().filter(v -> !existList.contains(v)).map(v -> {
                        RContractStore contractStore = new RContractStore();
                        contractStore.setContractId(v.getContractId());
                        contractStore.setStoreId(sourceId);
                        return contractStore;
                    }).collect(Collectors.toList());
                    contractStoreService.saveBatch(insList);
                    contractStoreService.lambdaUpdate().eq(RContractStore::getStoreId, targetId).remove();
                });

        // 工单相关合并
        taskMapper.update(new Task(), Wrappers.lambdaUpdate(Task.class).set(Task::getStoreId, sourceId).eq(Task::getStoreId, targetId));
        fileService.lambdaUpdate().set(FileInfo::getStoreId, sourceId).eq(FileInfo::getStoreId, targetId).eq(FileInfo::getSourceType, 3).update();
        faultLogService.lambdaUpdate().set(FaultLog::getStoreId, sourceId).eq(FaultLog::getStoreId, targetId).update();
        formService.lambdaUpdate().set(Form::getStoreId, sourceId).set(Form::getAccountId, store.getAccountId()).eq(Form::getStoreId, targetId).update(new Form());
        inspectService.lambdaUpdate().set(Inspect::getStoreId, sourceId).set(Inspect::getAccountId, store.getAccountId()).eq(Inspect::getStoreId, targetId).update(new Inspect());
        // 删除 targetId 对应的项目
        this.removeById(targetId);
        return "合并成功";
    }

    @Override
    public String addTag(Long id, Long tagId) {
        RStoreTag storeTag = new RStoreTag();
        storeTag.setStoreId(id);
        storeTag.setTagId(tagId);
        return storeTagService.save(storeTag) ? "添加成功" : "添加失败";
    }
}