# CbCaseIwcAdRequestServiceImpl.java

    package cloud.wconnect.finance.service.chargeback.impl;

    import cloud.wconnect.commonlib.component.BeanMapper;
    import cloud.wconnect.commonlib.cons.BaseErrorCodeEnum;
    import cloud.wconnect.commonlib.cons.CommonErrorI18nConstant;
    import cloud.wconnect.commonlib.cons.OperationAdjCons;
    import cloud.wconnect.commonlib.cons.SystemPlatformEnum;
    import cloud.wconnect.commonlib.exception.BadRequestException;
    import cloud.wconnect.commonlib.exception.ForbiddenException;
    import cloud.wconnect.commonlib.pojo.bo.adj.AdjBO;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.pojo.dto.result.ResultDTO;
    import cloud.wconnect.commonlib.service.UserInternalService;
    import cloud.wconnect.finance.cons.chargeback.CaseErrorCons;
    import cloud.wconnect.finance.entity.chargeback.CbAdj;
    import cloud.wconnect.finance.entity.chargeback.CbCaseIwcAdRequest;
    import cloud.wconnect.finance.entity.chargeback.CbCaseIwcAdRequestComment;
    import cloud.wconnect.finance.mapper.chargeback.CbCaseIwcAdRequestMapper;
    import cloud.wconnect.finance.pojo.dto.chargeback.CbCaseIwcAdRequestDTO;
    import cloud.wconnect.finance.pojo.query.chargeback.ExistSameAdjQuery;
    import cloud.wconnect.finance.service.chargeback.ICbAdjService;
    import cloud.wconnect.finance.service.chargeback.ICbCaseIwcAdRequestCommentService;
    import cloud.wconnect.finance.service.chargeback.ICbCaseIwcAdRequestService;
    import com.baomidou.dynamic.datasource.annotation.DS;
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.google.common.collect.Lists;
    import org.apache.commons.collections4.CollectionUtils;
    import org.apache.commons.lang3.StringUtils;
    import org.checkerframework.checker.nullness.qual.Nullable;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Lazy;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;

    import java.text.MessageFormat;
    import java.util.*;
    import java.util.function.Function;
    import java.util.stream.Collectors;

    /**
     * <p>
     * 服务实现类
     * </p>
     *
     * @author lichen
     * @since 2026-02-09
     */
    @Service
    @DS("finance")
    public class CbCaseIwcAdRequestServiceImpl extends ServiceImpl<CbCaseIwcAdRequestMapper, CbCaseIwcAdRequest> implements ICbCaseIwcAdRequestService {

        @Autowired
        private BeanMapper beanMapper;
        @Autowired
        private ICbCaseIwcAdRequestCommentService cbCaseIwcAdRequestCommentService;
        @Autowired
        private UserInternalService userInternalService;
        @Autowired
        @Lazy
        private ICbAdjService cbAdjService;

        /**
         * 保存数据
         *
         * @param propertyId 物业id
         * @param adjBOList  AdjBO列表
         * @param syncTime   同步时间
         * @author lichen
         * @date 2026-02-09
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void save(int propertyId, List<AdjBO> adjBOList, long syncTime) {
            if (CollectionUtils.isEmpty(adjBOList)) {
                return;
            }
            List<CbCaseIwcAdRequest> adjustmentList = beanMapper.mapList(adjBOList, CbCaseIwcAdRequest.class,
                    (bo, item) -> {
                        item.setPropertyId(propertyId);
                        item.setSyncTime(syncTime);
                    });

            // 按唯一索引字段排序，避免死锁
            adjustmentList.sort(Comparator.comparing(CbCaseIwcAdRequest::getAdjustmentId));

            baseMapper.insertOrUpdateBatchByUnique(adjustmentList);

            // adj Number集合
            List<String> adjNumbers = adjBOList.stream().map(AdjBO::getAdjNumber).collect(Collectors.toList());
            Map<String, AdjBO> adjNumber2AdjBO = adjBOList.stream().collect(Collectors.toMap(AdjBO::getAdjNumber, Function.identity()));
            // 查询Cb Adj集合
            List<CbAdj> cbAdjs = cbAdjService.listByAdjNumbers(adjNumbers);
            if (CollectionUtils.isNotEmpty(cbAdjs)) {
                ArrayList<CbAdj> updateBatch = Lists.newArrayListWithCapacity(cbAdjs.size());
                for (CbAdj cbAdj : cbAdjs) {
                    AdjBO adjBO = adjNumber2AdjBO.get(cbAdj.getAdjNumber());

                    CbAdj updateEntity = new CbAdj();
                    updateEntity.setId(cbAdj.getId());
                    updateEntity.setGuestName(adjBO.getGuestName());
                    updateEntity.setRoomNumber(adjBO.getRoomNumber());
                    updateEntity.setConfNumber(adjBO.getConfNumber());
                    updateBatch.add(updateEntity);
                }
                cbAdjService.updateBatchById(updateBatch);
            }
        }

        /**
         * 删除数据
         *
         * @param propertyId 物业id
         * @param adjBOList  AdjBO列表
         * @author lichen
         * @date 2026-02-09
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void delete(int propertyId, List<AdjBO> adjBOList) {
            if (CollectionUtils.isEmpty(adjBOList)) {
                return;
            }

            List<Long> adjustmentIds = adjBOList.stream()
                    .map(AdjBO::getAdjustmentId).collect(Collectors.toList());

            // 删除调账数据
            remove(new QueryWrapper<CbCaseIwcAdRequest>().lambda()
                    .eq(CbCaseIwcAdRequest::getPropertyId, propertyId)
                    .in(CbCaseIwcAdRequest::getAdjustmentId, adjustmentIds));

            // 删除调账数据备注
            cbCaseIwcAdRequestCommentService.removeByAdjustmentIds(propertyId, adjustmentIds);
        }

        /**
         * 检查数据是否存在
         *
         * @param adjNumbers Adj编号列表
         * @return java.util.List<cloud.wconnect.finance.entity.chargeback.CbCaseIwcAdRequest>
         * @author lichen
         * @date 2026/2/9
         */
        @Override
        public List<CbCaseIwcAdRequest> checkExist(List<String> adjNumbers) {
            if (CollectionUtils.isEmpty(adjNumbers)) return Collections.emptyList();
            // 获取已存在的数据
            List<CbCaseIwcAdRequest> existList = list(new LambdaQueryWrapper<CbCaseIwcAdRequest>().in(CbCaseIwcAdRequest::getAdjNumber, adjNumbers));
            if (existList.size() != adjNumbers.size()) {
                List<String> existAdjNumbers = existList.stream().map(CbCaseIwcAdRequest::getAdjNumber).toList();
                List<String> notExistAdjNumbers = adjNumbers.stream().filter(item -> !existAdjNumbers.contains(item)).toList();
                throw new BadRequestException(CommonErrorI18nConstant.DATA_NOT_FOUND,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "ChargeBack bind adjustment requests not found, notExistAdjNumbers = " + notExistAdjNumbers);
            }
            return existList;
        }

        /**
         * 检查数据归属
         *
         * @param cbCaseIwcAdRequests CbCaseIwcAdRequest列表
         * @param propertyId          资产id
         * @author lichen
         * @date 2026/2/9
         */
        @Override
        public void checkBelong(List<CbCaseIwcAdRequest> cbCaseIwcAdRequests, Integer propertyId) {
            if (cbCaseIwcAdRequests.stream().anyMatch(item -> !item.getPropertyId().equals(propertyId))) {
                List<String> adjNumbers = cbCaseIwcAdRequests.stream().map(CbCaseIwcAdRequest::getAdjNumber).toList();
                throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                        BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(),
                        MessageFormat.format("ChargeBack bind adjustment requests not belong currentPropertyId, bindAdjNumbers:{0},currentPropertyId:{1}",
                                adjNumbers, propertyId));
            }
        }

        /**
         * 检查数据是否已经绑定
         *
         * @param cbCaseIwcAdRequests CbCaseIwcAdRequest列表
         * @param caseNumber          Case Number
         * @author lichen
         * @date 2026/2/9
         */
        @Override
        public void checkAlreadyBind(List<CbCaseIwcAdRequest> cbCaseIwcAdRequests, String caseNumber) {
            if (cbCaseIwcAdRequests.stream().anyMatch(item ->
                    StringUtils.isNotBlank(item.getCaseNumber()) && !item.getCaseNumber().equals(caseNumber))) {
                throw new BadRequestException(CaseErrorCons.ADJUSTMENT_ALREADY_BIND_CASE,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "Adjustment already bind case");
            }
        }

        /**
         * 获取已经存在的相同Adj的数据
         *
         * @param propertyDTO       资产DTO
         * @param existSameAdjQuery 查询参数
         * @return cloud.wconnect.commonlib.pojo.dto.result.ResultDTO<java.util.List < cloud.wconnect.finance.pojo.dto.chargeback.CbCaseIwcAdRequestDTO>>
         * @author lichen
         * @date 2026/2/10
         */
        @Override
        public ResultDTO<List<CbCaseIwcAdRequestDTO>> listExistSameAdj(PropertyDTO propertyDTO, ExistSameAdjQuery existSameAdjQuery) {
            ArrayList<CbCaseIwcAdRequest> resultList = Lists.newArrayList();
            // 查询修改的CbAdj绑定的数据
            String ignoreAdjNumber = "";
            if (Objects.nonNull(existSameAdjQuery.getCbAdjId())) {
                CbAdj cbAdj = cbAdjService.getById(existSameAdjQuery.getCbAdjId());
                if (cbAdj.getType() == CbAdj.TYPE_BIND) {
                    CbCaseIwcAdRequest cbCaseIwcAdRequest = getByAdjNumber(cbAdj.getAdjNumber());
                    ignoreAdjNumber = cbAdj.getAdjNumber();
                    resultList.add(cbCaseIwcAdRequest);
                }
            }

            LambdaQueryWrapper<CbCaseIwcAdRequest> queryWrapper;
            String pmsType = propertyDTO.getPmsType();
            if (SystemPlatformEnum.OPERA.getDesc().equals(pmsType)) {
                queryWrapper = new LambdaQueryWrapper<CbCaseIwcAdRequest>()
                        .eq(CbCaseIwcAdRequest::getPropertyId, propertyDTO.getId())
                        .eq(CbCaseIwcAdRequest::getRoomNumber, existSameAdjQuery.getRoomNumber())
                        .eq(CbCaseIwcAdRequest::getTrxCode, existSameAdjQuery.getTrxCode())
                        .eq(CbCaseIwcAdRequest::getAdjustedDollar, existSameAdjQuery.getAdjustedAmount());

                String guestName = existSameAdjQuery.getGuestName();
                if (guestName.contains(",")) {
                    String[] names = guestName.split(",");
                    queryWrapper.and(childQw ->
                            childQw.eq(CbCaseIwcAdRequest::getGuestName, names[0] + "," + names[1])
                                    .or().eq(CbCaseIwcAdRequest::getGuestName, names[1] + "," + names[0]));
                } else {
                    queryWrapper.eq(CbCaseIwcAdRequest::getGuestName, existSameAdjQuery.getGuestName());
                }

            } else {
                queryWrapper = new LambdaQueryWrapper<CbCaseIwcAdRequest>().eq(CbCaseIwcAdRequest::getPropertyId, propertyDTO.getId())
                        .eq(CbCaseIwcAdRequest::getConfNumber, existSameAdjQuery.getConfNumber())
                        .eq(CbCaseIwcAdRequest::getTrxCode, existSameAdjQuery.getTrxCode())
                        .eq(CbCaseIwcAdRequest::getAdjustedDollar, existSameAdjQuery.getAdjustedAmount());
            }
            // 只查询未绑定Cb Adj的数据
            queryWrapper.eq(CbCaseIwcAdRequest::getCbAdjId, 0);
            // 忽略指定数据
            if(StringUtils.isNotBlank(ignoreAdjNumber)){
                queryWrapper.ne(CbCaseIwcAdRequest::getAdjNumber, ignoreAdjNumber);
            }
            // 查询已经存在的相同Adj数据
            List<CbCaseIwcAdRequest> existSameAdjs = list(queryWrapper);

            resultList.addAll(existSameAdjs);

            if (CollectionUtils.isEmpty(resultList)) return ResultDTO.success(Collections.emptyList());

            List<Long> adjustmentIds = resultList.stream().map(CbCaseIwcAdRequest::getAdjustmentId).collect(Collectors.toList());
            Map<Long, Long> adjustmentId2Count = cbCaseIwcAdRequestCommentService.listByAdjustmentIds(propertyDTO.getId(), adjustmentIds)
                    .stream().collect(Collectors.groupingBy(CbCaseIwcAdRequestComment::getArId, Collectors.counting()));

            Set<Integer> userIds = resultList.stream().map(CbCaseIwcAdRequest::getDutyUser).collect(Collectors.toSet());
            Map<Integer, String> userId2Name = userInternalService.getNameMapByIds(propertyDTO.getTenantId(), userIds);

            List<CbCaseIwcAdRequestDTO> cbCaseIwcAdRequestDTOS = beanMapper.mapList(resultList, CbCaseIwcAdRequestDTO.class, (entity, dto) -> {
                dto.setDutyUserName(userId2Name.get(entity.getDutyUser()));
                dto.setCommentsCount(adjustmentId2Count.getOrDefault(entity.getAdjustmentId(), 0L));
            });
            return ResultDTO.success(cbCaseIwcAdRequestDTOS);
        }

        /**
         * 根据Adj编号获取数据
         *
         * @param adjNumber Adj编号
         * @return cloud.wconnect.finance.entity.chargeback.CbCaseIwcAdRequest
         * @author lichen
         * @date 2026/2/11
         */
        @Override
        public CbCaseIwcAdRequest getByAdjNumber(String adjNumber) {
            return getOne(new QueryWrapper<CbCaseIwcAdRequest>().lambda().eq(CbCaseIwcAdRequest::getAdjNumber, adjNumber));
        }

    }