# AdjApproveConfigServiceImpl.java

    package cloud.wconnect.frontoffice.service.adjustment.impl;

    import cloud.wconnect.commonlib.cons.BaseConstant;
    import cloud.wconnect.commonlib.cons.BaseErrorCodeEnum;
    import cloud.wconnect.commonlib.cons.CommonErrorI18nConstant;
    import cloud.wconnect.commonlib.cons.DataPermissionConfigConstant;
    import cloud.wconnect.commonlib.exception.BadRequestException;
    import cloud.wconnect.commonlib.pojo.dto.UserDTO;
    import cloud.wconnect.commonlib.pojo.dto.result.ResultDTO;
    import cloud.wconnect.commonlib.redis.PropertyI18nRedisService;
    import cloud.wconnect.commonlib.service.DataPermissionConfigInternalService;
    import cloud.wconnect.commonlib.service.RoleInternalService;
    import cloud.wconnect.commonlib.service.UserInternalService;
    import cloud.wconnect.frontoffice.cons.adjustment.AdjTypeEnum;
    import cloud.wconnect.frontoffice.entity.adjustment.AdRequest;
    import cloud.wconnect.frontoffice.entity.adjustment.AdjApproveConfig;
    import cloud.wconnect.frontoffice.entity.adjustment.AdjOutOfOfficeConfig;
    import cloud.wconnect.frontoffice.mapper.adjustment.AdjApproveConfigMapper;
    import cloud.wconnect.frontoffice.pojo.dto.adjustment.UserIsApprover;
    import cloud.wconnect.frontoffice.service.adjustment.IAdjApproveConfigService;
    import cloud.wconnect.frontoffice.service.adjustment.IAdjCheckUserEffectiveService;
    import cloud.wconnect.frontoffice.service.adjustment.IAdjOutOfOfficeConfigService;
    import com.baomidou.dynamic.datasource.annotation.DS;
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.google.common.collect.ImmutableMap;
    import com.google.common.collect.Lists;
    import com.google.common.collect.Sets;
    import org.apache.commons.collections4.CollectionUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Lazy;
    import org.springframework.stereotype.Service;

    import java.util.*;
    import java.util.stream.Collectors;

    /**
     * <p>
     * Adjustment Requests 审批配置表 服务实现类
     * </p>
     *
     * @author lichen
     * @since 2023-03-30
     */
    @Service
    @DS("logbook")
    public class AdjApproveConfigServiceImpl extends ServiceImpl<AdjApproveConfigMapper, AdjApproveConfig> implements IAdjApproveConfigService {

        @Autowired
        private UserInternalService userInternalService;
        @Autowired
        private PropertyI18nRedisService propertyI18nRedisService;
        @Autowired
        private DataPermissionConfigInternalService dataPermissionConfigInternalService;
        @Lazy
        @Autowired
        private IAdjOutOfOfficeConfigService adjOutOfOfficeConfigService;
        @Autowired
        private IAdjCheckUserEffectiveService adjCheckUserEffectiveService;
        @Autowired
        private RoleInternalService roleInternalService;

        @Override
        public Set<Integer> getApproveUsers(Integer propertyId, Long levelId) {
            LambdaQueryWrapper<AdjApproveConfig> approveConfigQueryWrapper = new LambdaQueryWrapper<>();
            approveConfigQueryWrapper.eq(AdjApproveConfig::getPropertyId, propertyId);
            approveConfigQueryWrapper.eq(AdjApproveConfig::getLevelId, levelId);
            List<AdjApproveConfig> list = list(approveConfigQueryWrapper);
            if (CollectionUtils.isEmpty(list)) return Collections.emptySet();
            return list.stream().map(AdjApproveConfig::getUserId).collect(Collectors.toSet());
        }

        /**
         * 查询审批层级配置集合
         *
         * @param propertyId 资产id
         * @param levelIds   审批层级id集合
         * @return java.util.List<cloud.wconnect.frontoffice.entity.adjustment.AdjApproveConfig>
         * @author lichen
         * @date 2023/9/8
         */
        @Override
        public List<AdjApproveConfig> listByLevelIds(Integer propertyId, List<Long> levelIds) {
            if (CollectionUtils.isEmpty(levelIds)) return Collections.emptyList();
            return list(new LambdaQueryWrapper<AdjApproveConfig>()
                    .eq(AdjApproveConfig::getPropertyId, propertyId)
                    .in(AdjApproveConfig::getLevelId, levelIds));
        }

        /**
         * 根据用户ID查询审批配置
         *
         * @param propertyId 资产ID
         * @param userId     用户ID
         * @return java.util.List<cloud.wconnect.frontoffice.entity.adjustment.AdjApproveConfig>
         * @author lichen
         * @date 2023/12/11
         */
        @Override
        public List<AdjApproveConfig> getApproveConfigByUserId(Integer propertyId, int userId) {
            return list(new LambdaQueryWrapper<AdjApproveConfig>()
                    .eq(AdjApproveConfig::getPropertyId, propertyId)
                    .eq(AdjApproveConfig::getUserId, userId));
        }

        /**
         * 查询用户归属的审批配置
         *
         * @param propertyId  资产Id
         * @param requestType 类型
         * @param userId      用户Id
         * @return cloud.wconnect.frontoffice.entity.adjustment.AdjApproveConfig
         * @author lichen
         * @date 2024/1/29
         */
        @Override
        public AdjApproveConfig getByUserId(Integer propertyId, Integer requestType, int userId) {
            LambdaQueryWrapper<AdjApproveConfig> approveConfigQueryWrapper = new LambdaQueryWrapper<>();
            approveConfigQueryWrapper.eq(AdjApproveConfig::getPropertyId, propertyId);
            approveConfigQueryWrapper.eq(AdjApproveConfig::getUserId, userId);
            approveConfigQueryWrapper.eq(AdjApproveConfig::getType, requestType);
            approveConfigQueryWrapper.orderByAsc(AdjApproveConfig::getApproveIndex);
            approveConfigQueryWrapper.last("limit 1");
            return getOne(approveConfigQueryWrapper);
        }

        /**
         * 根据审批级别Id查询审批人
         *
         * @param propertyId 资产Id
         * @param levelId    审批级别Id
         * @return java.util.List<java.lang.Integer>
         * @author lichen
         * @date 2024/1/29
         */
        @Override
        public List<Integer> listApproverByLevelId(Integer propertyId, Long levelId) {
            LambdaQueryWrapper<AdjApproveConfig> configLambdaQueryWrapper = new LambdaQueryWrapper<>();
            configLambdaQueryWrapper.eq(AdjApproveConfig::getPropertyId, propertyId);
            configLambdaQueryWrapper.eq(AdjApproveConfig::getLevelId, levelId);
            List<AdjApproveConfig> needApproveConfigs = list(configLambdaQueryWrapper);
            return needApproveConfigs.stream().map(AdjApproveConfig::getUserId).collect(Collectors.toList());
        }

        /**
         * 根据审批级别Id查询审批人
         *
         * @param propertyId       资产 Id
         * @param approvalLevelIds 审批级别 Id
         * @return java.util.List<java.lang.Integer>
         * @author yyt
         * @date 2026/3/18
         */
        @Override
        public List<AdjApproveConfig> listApproverByLevelIds(Integer propertyId, List<Long> approvalLevelIds) {
            return list(new LambdaQueryWrapper<AdjApproveConfig>()
                    .eq(AdjApproveConfig::getPropertyId, propertyId)
                    .in(AdjApproveConfig::getLevelId, approvalLevelIds));
        }

        /**
         * 校验是否指定类型的审批人
         *
         * @param propertyId 资产id
         * @param userId     用户id
         * @param adRequest  调账
         * @return boolean
         * @author lichen
         * @date 2024/12/20
         */
        @Override
        public boolean checkSpecificTypeApprover(Integer propertyId, int userId, AdRequest adRequest) {
            Integer tenantId = propertyI18nRedisService.get(propertyId).getTenantId();
            Integer requestType = adRequest.getRequestType();
            AdjApproveConfig adjApproveConfig = getOne(new LambdaQueryWrapper<AdjApproveConfig>()
                    .eq(AdjApproveConfig::getPropertyId, propertyId)
                    .eq(AdjApproveConfig::getType, requestType)
                    .eq(AdjApproveConfig::getUserId, userId));
            if (Objects.nonNull(adjApproveConfig)) {
                // 检查登陆用户是否无审批权限
                return dataPermissionConfigInternalService.checkContainsApi(tenantId, propertyId,
                                ImmutableMap.of(DataPermissionConfigConstant.IDENTITY_TYPE_USER, Sets.newHashSet(userId)),
                                DataPermissionConfigConstant.ADJ_APPROVE_API_ID)
                        .getOrDefault(DataPermissionConfigConstant.IDENTITY_TYPE_USER, Collections.emptyMap())
                        .getOrDefault(userId, Boolean.FALSE);
            } else {
                // 检查登陆用户是否是补位人员
                // 查询 adj 所属审批层级下的用户（过滤掉禁用用户和没有审批权限的用户），看看当前登陆用户是否是审批层级下的用户配置的补位人员
                List<Integer> approvers;
                Long approveLevelId = adRequest.getApproveLevelId();
                if (approveLevelId == BaseConstant.LONG_ZERO) {
                    // 查询当前类型下的所有审批人
                    approvers = listByType(propertyId, adRequest.getRequestType());
                } else {
                    approvers = listApproverByLevelId(propertyId, approveLevelId);
                }
                if (CollectionUtils.isEmpty(approvers)) return false;
                approvers = adjCheckUserEffectiveService.checkUserEffective(propertyId, approvers);
                if (CollectionUtils.isEmpty(approvers)) return false;

                List<Integer> substitutePersonnelIds = adjOutOfOfficeConfigService.listEffectiveConfigForApprover(propertyId, requestType, approvers)
                        .stream().map(AdjOutOfOfficeConfig::getSubstitutePersonnelId).toList();
                return substitutePersonnelIds.contains(userId);
            }
        }

        /**
         * 查询指定类型下的审批人
         *
         * @param propertyId  资产 id
         * @param requestType 类型
         * @return 审批人
         * @author yyt
         * @date 2026/3/23
         **/
        private List<Integer> listByType(Integer propertyId, Integer requestType) {
            return list(new LambdaQueryWrapper<AdjApproveConfig>()
                    .eq(AdjApproveConfig::getPropertyId, propertyId)
                    .eq(AdjApproveConfig::getType, requestType)
                    .select(AdjApproveConfig::getUserId))
                    .stream().map(AdjApproveConfig::getUserId).toList();
        }

        /**
         * 查询用户的审批配置
         *
         * @param propertyId 资产ID
         * @param userId     用户ID
         * @return java.util.List<cloud.wconnect.frontoffice.entity.adjustment.AdjApproveConfig>
         * @author lichen
         * @date 2024/12/20
         */
        @Override
        public List<AdjApproveConfig> listByUserId(Integer propertyId, Integer userId) {
            return list(new LambdaQueryWrapper<AdjApproveConfig>()
                    .eq(AdjApproveConfig::getPropertyId, propertyId)
                    .eq(AdjApproveConfig::getUserId, userId));
        }

        /**
         * 批量查询审批人
         *
         * @param propertyId 资产ID
         * @param userIds     用户ID
         * @return 审批人
         * @author yyt
         * @date 2026/3/19
         */
        @Override
        public List<AdjApproveConfig> listByUserIds(Integer propertyId, List<Integer> userIds) {
            return list(new LambdaQueryWrapper<AdjApproveConfig>()
                    .eq(AdjApproveConfig::getPropertyId, propertyId)
                    .in(AdjApproveConfig::getUserId, userIds));
        }

        /**
         * 校验用户是否重复
         *
         * @param propertyId 资产ID
         * @param type       类型
         * @param userIds    用户ID
         * @param ignoreId   忽略的id
         * @author lichen
         * @date 2024/12/20
         */
        @Override
        public void checkUserRepeat(int propertyId, Integer type, List<Integer> userIds, Long ignoreId) {
            //校验用户是否已经配置
            LambdaQueryWrapper<AdjApproveConfig> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(AdjApproveConfig::getPropertyId, propertyId);
            queryWrapper.eq(AdjApproveConfig::getType, type);
            queryWrapper.ne(Objects.nonNull(ignoreId), AdjApproveConfig::getLevelId, ignoreId);
            List<AdjApproveConfig> approvedConfigs = list(queryWrapper);
            if (CollectionUtils.isNotEmpty(approvedConfigs)) {
                //配置的用户id集合
                List<Integer> configUserIds = approvedConfigs.stream().map(AdjApproveConfig::getUserId).collect(Collectors.toList());
                //两个集合有交集
                if (CollectionUtils.containsAny(configUserIds, userIds)) {
                    throw new BadRequestException(CommonErrorI18nConstant.APPROVE_USER_ALREADY_EXIST,
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            "user already exist approve setting");
                }
            }
        }

        /**
         * 校验是否有失效人员
         *
         * @param propertyId 资产 id
         * @return true:有失效人员，false: 无失效人员
         * @author yyt
         * @date 2026/3/18
         */
        @Override
        public ResultDTO<Boolean> checkHasFailurePersonnel(int propertyId) {
            // 1.检查是否有状态是关闭的用户在审批配置上
            // 2.检查审批配置上的用户是否已失去审批角色
            Set<Integer> userIds = list(new LambdaQueryWrapper<AdjApproveConfig>()
                    .eq(AdjApproveConfig::getPropertyId, propertyId)
                    .select(AdjApproveConfig::getUserId))
                    .stream().map(AdjApproveConfig::getUserId).collect(Collectors.toSet());
            if (CollectionUtils.isEmpty(userIds)) return ResultDTO.success(false);

            Integer tenantId = propertyI18nRedisService.get(propertyId).getTenantId();
            boolean hasFailurePersonnel = userInternalService.getUsers(tenantId, userIds).stream().anyMatch(item -> !item.getStatus());
            if (hasFailurePersonnel) {
                return ResultDTO.success(true);
            }

            Map<Integer, Map<Integer, Boolean>> hasPermissionByType = dataPermissionConfigInternalService.checkContainsApi(tenantId, propertyId,
                    ImmutableMap.of(DataPermissionConfigConstant.IDENTITY_TYPE_USER, userIds), DataPermissionConfigConstant.ADJ_APPROVE_API_ID);
            Map<Integer, Boolean> hasPermissionByUserId = hasPermissionByType.getOrDefault(DataPermissionConfigConstant.IDENTITY_TYPE_USER, Collections.emptyMap());
            for (Map.Entry<Integer, Boolean> entry : hasPermissionByUserId.entrySet()) {
                if (!entry.getValue()) {
                    hasFailurePersonnel = true;
                    break;
                }
            }
            return ResultDTO.success(hasFailurePersonnel);
        }

        /**
         * 获取最小的审批层级的审批人
         *
         * @param propertyId  资产id
         * @param type        类型
         * @param approverIds 审批人id
         * @return 审批人
         * @author yyt
         * @date 2026/3/19
         */
        @Override
        public List<AdjApproveConfig> getMinLevelConfigByUserIds(Integer propertyId, Integer type, List<Integer> approverIds) {
            List<AdjApproveConfig> configs = list(new LambdaQueryWrapper<AdjApproveConfig>()
                    .eq(AdjApproveConfig::getPropertyId, propertyId)
                    .in(AdjApproveConfig::getUserId, approverIds)
                    .orderByAsc(AdjApproveConfig::getApproveIndex));
            if (CollectionUtils.isEmpty(configs)) return Collections.emptyList();

            configs.sort(Comparator.comparing(AdjApproveConfig::getApproveIndex));
            Integer minApproveIndex = configs.get(0).getApproveIndex();
            return configs.stream().filter(item -> Objects.equals(item.getApproveIndex(), minApproveIndex)).collect(Collectors.toList());
        }

        /**
         * 获取资产下有审批权限的用户
         *
         * @param tenantId   租户id
         * @param propertyId 资产id
         * @param type       类型
         * @return java.util.List<cloud.wconnect.commonlib.pojo.dto.UserDTO>
         * @author yyt
         * @date 2026/3/19
         */
        @Override
        public List<UserDTO> listHasApprovePermissionUser(Integer tenantId, int propertyId, Integer type) {
            List<UserDTO> allApprovers = roleInternalService.listUser(tenantId, propertyId, BaseConstant.ADJUSTMENT_APPROVE_ROLES);
            if (Objects.isNull(type)) {
                return allApprovers;
            }

            // 返回不在当前类型下的审批树上的用户
            List<Integer> inApproveLevelUsers = list(new LambdaQueryWrapper<AdjApproveConfig>()
                    .eq(AdjApproveConfig::getPropertyId, propertyId)
                    .eq(AdjApproveConfig::getType, type)
                    .select(AdjApproveConfig::getUserId))
                    .stream().map(AdjApproveConfig::getUserId).toList();
            return allApprovers.stream().filter(item -> !inApproveLevelUsers.contains(item.getId())).toList();
        }

        /**
         * 获取用户是否是审批人
         *
         * @param propertyId 资产id
         * @param userId     用户id
         * @return cloud.wconnect.commonlib.pojo.dto.UserIsApprover
         * @author yyt
         * @date 2026/3/19
         */
        @Override
        public UserIsApprover getUserIsApprover(int propertyId, Integer userId) {
            // 检查用户是否有效（1.状态开启，2.有审批权限）
            List<Integer> effective = adjCheckUserEffectiveService.checkUserEffective(propertyId, Lists.newArrayList(userId));
            if (CollectionUtils.isEmpty(effective)) return new UserIsApprover(false, false, false);

            // 用户有效，检查用户是审批人还是补位审批人
            List<AdjApproveConfig> approveConfigs = listByUserId(propertyId, userId);
            if (CollectionUtils.isEmpty(approveConfigs)) {
                // 不是审批人，检查是否是补位审批人
                List<Integer> types = adjOutOfOfficeConfigService.listEffectiveConfigForSupplementaryPersonnel(propertyId, userId)
                        .stream().map(AdjOutOfOfficeConfig::getType).toList();
                return new UserIsApprover(types.contains(AdjTypeEnum.SALES.getAdjType()), types.contains(AdjTypeEnum.FRONT_DESK.getAdjType()), types.contains(AdjTypeEnum.ACCOUNTING.getAdjType()));
            } else {
                List<Integer> typeList = approveConfigs.stream().map(AdjApproveConfig::getType).toList();
                UserIsApprover userIsApprover = new UserIsApprover();
                userIsApprover.setSalesApprover(typeList.contains(AdjTypeEnum.SALES.getAdjType()));
                userIsApprover.setFrontDeskApprover(typeList.contains(AdjTypeEnum.FRONT_DESK.getAdjType()));
                userIsApprover.setAccountingApprover(typeList.contains(AdjTypeEnum.ACCOUNTING.getAdjType()));
                return userIsApprover;
            }
        }

    }