# AdjApproveConfigController.java

    package cloud.wconnect.frontoffice.controller.adjustment;

    import cloud.wconnect.commonlib.annotation.ApiId;
    import cloud.wconnect.commonlib.component.SessionContext;
    import cloud.wconnect.commonlib.cons.*;
    import cloud.wconnect.commonlib.exception.BadRequestException;
    import cloud.wconnect.commonlib.exception.ForbiddenException;
    import cloud.wconnect.commonlib.pojo.dto.UserDTO;
    import cloud.wconnect.commonlib.pojo.dto.UsergroupDTO;
    import cloud.wconnect.commonlib.pojo.dto.result.ResultDTO;
    import cloud.wconnect.commonlib.service.DataPermissionConfigInternalService;
    import cloud.wconnect.commonlib.service.UserInternalService;
    import cloud.wconnect.commonlib.service.UsergroupInternalService;
    import cloud.wconnect.commonlib.util.I18nMessageUtils;
    import cloud.wconnect.frontoffice.entity.adjustment.AdjApproveLevel;
    import cloud.wconnect.frontoffice.pojo.dto.adjustment.AdjApproveCCDTO;
    import cloud.wconnect.frontoffice.pojo.dto.adjustment.AdjApproveLevelDTO;
    import cloud.wconnect.frontoffice.pojo.dto.adjustment.AdjApproveUser;
    import cloud.wconnect.frontoffice.pojo.dto.adjustment.UserIsApprover;
    import cloud.wconnect.frontoffice.service.adjustment.IAdjApproveConfigService;
    import cloud.wconnect.frontoffice.service.adjustment.IAdjApproveLevelService;
    import com.google.common.collect.ImmutableMap;
    import com.google.common.collect.Sets;
    import jakarta.validation.Valid;
    import org.apache.commons.collections4.CollectionUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.*;

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

    /**
     * <p>
     * Adjustment Requests 审批配置表 前端控制器
     * </p>
     *
     * @author lichen
     * @since 2023-03-30
     */
    @RestController
    @RequestMapping("/adjustment/approve-config")
    public class AdjApproveConfigController {

        @Autowired
        private IAdjApproveLevelService approveLevelService;
        @Autowired
        private IAdjApproveConfigService approveConfigService;
        @Autowired
        private SessionContext sessionContext;
        @Autowired
        private UserInternalService userInternalService;
        @Autowired
        private UsergroupInternalService usergroupInternalService;
        @Autowired
        private DataPermissionConfigInternalService dataPermissionConfigInternalService;
        @Autowired
        private I18nMessageUtils i18nMessageUtils;

        /**
         * @param property 资产标识
         * @param type     类型(1:Sales 2:Front Desk 3:Accounting 4:Misposting)
         * @return java.util.List<cloud.wconnect.frontoffice.pojo.dto.adjustment.AdjApproveLevelDTO>
         * @description 查询审批层级
         * @author lichen
         * @date 2023/3/30 11:16
         */
        @ApiId(229)
        @GetMapping
        public List<AdjApproveLevelDTO> list(String property, @RequestParam(name = "type") Integer type) {
            int propertyId = sessionContext.getPropertyId(property);
            return approveLevelService.list(propertyId, type);
        }

        /**
         * 保存审批层级
         *
         * @param property           资产标识
         * @param id                 主键id
         * @param adjApproveLevelDTO 审批层级Dto
         * @return java.util.Map
         * @author lichen
         * @date 2025/8/18
         */
        @ApiId(230)
        @PostMapping
        public ResultDTO<Void> save(String property, @RequestParam(name = "id") Long id,
                        @Valid @RequestBody AdjApproveLevelDTO adjApproveLevelDTO) {
            int propertyId = sessionContext.getPropertyId(property);
            Integer tenantId = sessionContext.getTenantSymbolBO().getId();
            //获取当前保存的审核用户集合
            List<Integer> userIds = adjApproveLevelDTO.getUsers().stream().map(AdjApproveUser::getId).collect(Collectors.toList());

            //校验用户不能重复
            Set<Integer> userSet = Sets.newHashSetWithExpectedSize(userIds.size());
            userSet.addAll(userIds);
            if (userSet.size() != userIds.size()) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_CANNOT_REPEAT,
                        i18nMessageUtils.getErrorParamMapNotNeedTranslate("userIds"),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "approve user cannot repeat");
            }

            // 检查审批人是否有效（1.状态开启，2.有审批权限）
            List<UserDTO> approves = userInternalService.getUsers(tenantId, userIds).stream().filter(UserDTO::getStatus).toList();
            if(CollectionUtils.isEmpty(approves) || approves.size() != userIds.size()){
                throw new BadRequestException(CommonErrorI18nConstant.DATA_NOT_EXIST,
                        i18nMessageUtils.getErrorParamMapNotNeedTranslate("approver"),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "The approver is not exist");
            }
            Map<Integer, Boolean> hasPermissionByUserId = dataPermissionConfigInternalService.checkContainsApi(tenantId, propertyId,
                    ImmutableMap.of(DataPermissionConfigConstant.IDENTITY_TYPE_USER, approves.stream().map(UserDTO::getId).collect(Collectors.toSet())),
                    DataPermissionConfigConstant.ADJ_APPROVE_API_ID).getOrDefault(DataPermissionConfigConstant.IDENTITY_TYPE_USER, Collections.emptyMap());
            if (approves.stream().anyMatch(item -> !hasPermissionByUserId.getOrDefault(item.getId(), false))) {
                return ResultDTO.fail(BaseErrorCodeEnum.USER_NOT_HAVE_APPROVE_PERMISSION.getCode(),
                        i18nMessageUtils.getAllLanguageMsg(BaseErrorCodeEnum.USER_NOT_HAVE_APPROVE_PERMISSION.getI18nCode()));
            }

            //校验审批人和抄送对象
            checkApproverAndCC(tenantId, propertyId, userIds, adjApproveLevelDTO.getCcList());

            if (id > BaseConstant.LONG_ZERO) {
                // 校验用户是否重复
                approveConfigService.checkUserRepeat(propertyId, adjApproveLevelDTO.getType(), userIds, id);

                AdjApproveLevel oriApprove = checkExistAndBelong(id, propertyId);
                //index 不能修改
                adjApproveLevelDTO.setApproveIndex(oriApprove.getApproveIndex());
                //type 不能修改
                adjApproveLevelDTO.setType(oriApprove.getType());
                adjApproveLevelDTO.setId(id);
                adjApproveLevelDTO.setPropertyId(propertyId);
                //修改
                approveLevelService.update(oriApprove, adjApproveLevelDTO);
            } else {
                // 校验用户是否重复
                approveConfigService.checkUserRepeat(propertyId, adjApproveLevelDTO.getType(), userIds, null);

                //新增
                adjApproveLevelDTO.setPropertyId(propertyId);
                approveLevelService.insert(adjApproveLevelDTO);
            }
            return ResultDTO.success();
        }

        /**
         * 删除审批层级
         *
         * @param property 资产标识
         * @param id       主键id
         * @return java.util.Map
         * @author lichen
         * @date 2025/8/18
         */
        @ApiId(231)
        @DeleteMapping
        public Map remove(String property, @RequestParam(name = "id") Long id) {
            int propertyId = sessionContext.getPropertyId(property);
            AdjApproveLevel oriApprove = checkExistAndBelong(id, propertyId);
            approveLevelService.remove(oriApprove);
            return Collections.emptyMap();
        }

        /**
         * 查询审批角色用户
         *
         * @param property 资产标识
         * @param type 类型（1:Sales 2:Front Desk 3:Accounting），参数目的：查询不在当前类型下的审批树上的用户
         * @return java.util.List<cloud.wconnect.commonlib.pojo.dto.UserDTO>
         * @author lichen
         * @date 2025/8/18
         */
        @ApiId(287)
        @GetMapping("/approveUser")
        public List<UserDTO> approveUser(String property, @RequestParam(required = false) Integer type) {
            int propertyId = sessionContext.getPropertyId(property);
            Integer tenantId = sessionContext.getTenantSymbolBO().getId();
            return approveConfigService.listHasApprovePermissionUser(tenantId, propertyId, type);
        }

        /**
         * 查询用户是否审批人
         *
         * @param property 资产标识
         * @param userId   用户ID
         * @return cloud.wconnect.frontoffice.pojo.dto.adjustment.UserIsApprover
         * @author lichen
         * @date 2024/12/20
         */
        @ApiId(1314)
        @GetMapping("/{userId:\\d+}/is-approver")
        public UserIsApprover getUserIsApprover(String property, @PathVariable Integer userId) {
            int propertyId = sessionContext.getPropertyId(property);
            return approveConfigService.getUserIsApprover(propertyId, userId);
        }

        /**
         * 1707 - 检查审批层级中是否有失效人员
         *
         * @param property 资产标识
         * @return true:有失效人员，false: 无失效人员
         * @author yyt
         * @date 2026/3/18
         **/
        @ApiId(1707)
        @GetMapping("/has-failure-personnel")
        public ResultDTO<Boolean> checkHasFailurePersonnel(String property) {
            int propertyId = sessionContext.getPropertyId(property);
            return approveConfigService.checkHasFailurePersonnel(propertyId);
        }

        /**
         * @param id         主键id
         * @param propertyId 资产id
         * @return void
         * @description 校验数据id 和 资产id 是否匹配
         * @author lichen
         * @date 2023/3/17 10:24
         */
        private AdjApproveLevel checkExistAndBelong(Long id, Integer propertyId) {
            AdjApproveLevel byId = approveLevelService.getById(id);
            if (Objects.isNull(byId)) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_NOT_FOUND,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "id=" + id + " ,Adjustment Requests Approve Config not found");
            }
            if (byId.getPropertyId().compareTo(propertyId) != 0) {
                throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                        BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(),
                        MessageFormat.format("Adjustment approve level not belong current property, approveLevelId:{0}, approvelevelIdBelongPropertyId:{1}, currentPropertyId:{2}",
                                id, byId.getPropertyId(), propertyId));
            }
            return byId;
        }

        /**
         * 校验审批人和抄送对象是否存在
         *
         * @param propertyId   资产id
         * @param approverList 审批人集合
         * @param ccList       抄送人集合
         * @return void
         * @author lichen
         * @date 2024/3/11
         */
        private void checkApproverAndCC(Integer tenantId, int propertyId, List<Integer> approverList, List<AdjApproveCCDTO> ccList) {
            //按照用户来源分组
            Map<Integer, List<Integer>> source2IdentityIdList = ccList.stream()
                    .collect(Collectors.groupingBy(AdjApproveCCDTO::getSource, Collectors.mapping(AdjApproveCCDTO::getIdentityId, Collectors.toList())));
            List<Integer> userIdsByCC = source2IdentityIdList.getOrDefault(EmailConfigCons.SOURCE_USER, Collections.emptyList());
            List<Integer> usergroupIds = source2IdentityIdList.getOrDefault(EmailConfigCons.SOURCE_USERGROUP, Collections.emptyList());

            //校验是否重复
            if (userIdsByCC.size() != Sets.newHashSet(userIdsByCC).size()) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_CANNOT_REPEAT,
                        i18nMessageUtils.getErrorParamMapNotNeedTranslate("userIds"),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "user cannot repeat");
            }
            if (usergroupIds.size() != Sets.newHashSet(usergroupIds).size()) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_CANNOT_REPEAT,
                        i18nMessageUtils.getErrorParamMapNotNeedTranslate("userGroupIds"),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "usergroup cannot repeat");
            }
            List<String> manualEmails = ccList.stream().filter(cc -> cc.getSource() == EmailConfigCons.SOURCE_MANUAL)
                    .map(AdjApproveCCDTO::getEmail).collect(Collectors.toList());
            if (manualEmails.size() != Sets.newHashSet(manualEmails).size()) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_CANNOT_REPEAT,
                        i18nMessageUtils.getErrorParamMapNotNeedTranslate("manualEmails"),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "manual email cannot repeat");
            }

            //用户集合
            Set<Integer> allUserIds = new HashSet<>();
            allUserIds.addAll(approverList);
            allUserIds.addAll(userIdsByCC);
            List<UserDTO> allUsers = userInternalService.getUsers(tenantId, allUserIds);
            //校验是否存在
            if (allUserIds.size() != allUsers.size()) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_NOT_EXIST,
                        i18nMessageUtils.getErrorParamMapNotNeedTranslate("userId"),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The user not exist");
            }

            //校验用户组是否存在
            if (CollectionUtils.isNotEmpty(usergroupIds)) {
                List<UsergroupDTO> usergroups = usergroupInternalService.getUsergroups(tenantId, propertyId, usergroupIds);
                if (usergroups.size() != usergroupIds.size()) {
                    throw new BadRequestException(CommonErrorI18nConstant.DATA_NOT_EXIST,
                            i18nMessageUtils.getErrorParamMapNotNeedTranslate("userGroupId"),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            "The user group not exist");
                }
            }
        }
    }