# SvaEmailConfigController.java

    package cloud.wconnect.workperformance.controller.sva;

    import cloud.wconnect.commonlib.annotation.ApiId;
    import cloud.wconnect.commonlib.component.SessionContext;
    import cloud.wconnect.commonlib.cons.BaseErrorCodeEnum;
    import cloud.wconnect.commonlib.cons.CommonErrorI18nConstant;
    import cloud.wconnect.commonlib.cons.EmailConfigCons;
    import cloud.wconnect.commonlib.exception.BadRequestException;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.pojo.dto.UserDTO;
    import cloud.wconnect.commonlib.pojo.dto.UsergroupDTO;
    import cloud.wconnect.commonlib.redis.PropertyI18nRedisService;
    import cloud.wconnect.commonlib.service.UserInternalService;
    import cloud.wconnect.commonlib.service.UsergroupInternalService;
    import cloud.wconnect.commonlib.util.I18nMessageUtils;
    import cloud.wconnect.workperformance.cons.sva.SvaNotifyTypeEnum;
    import cloud.wconnect.workperformance.pojo.dto.sva.email.SvaEmailConfigDTO;
    import cloud.wconnect.workperformance.pojo.dto.sva.email.SvaEmailTargetDTO;
    import cloud.wconnect.workperformance.service.sva.scheduleactual.ISvaEmailConfigService;
    import com.google.common.collect.ImmutableMap;
    import com.google.common.collect.Lists;
    import com.google.common.collect.Sets;
    import jakarta.annotation.Resource;
    import jakarta.validation.Valid;
    import org.apache.commons.collections.CollectionUtils;
    import org.springframework.web.bind.annotation.*;

    import java.text.MessageFormat;
    import java.util.Collections;
    import java.util.List;
    import java.util.Map;
    import java.util.stream.Collectors;

    /**
     * SVA Email Config
     *
     * @author zc
     * @date 2024/03/14
     */
    @RestController
    @RequestMapping("/sva/schedule-actual/email-config")
    public class SvaEmailConfigController {
        @Resource
        private ISvaEmailConfigService emailConfigService;
        @Resource
        private UserInternalService userInternalService;
        @Resource
        private UsergroupInternalService usergroupInternalService;
        @Resource
        private PropertyI18nRedisService propertyI18nRedisService;
        @Resource
        private SessionContext sessionContext;
        @Resource
        private I18nMessageUtils i18nMessageUtils;

        /**
         * 获取邮件配置
         *
         * @param property 资产标识
         * @param type     通知类型
         * @return 邮件配置
         * @author zc
         * @date 2024/11/12
         */
        @ApiId(375)
        @GetMapping
        public SvaEmailConfigDTO get(@RequestParam String property, @RequestParam int type) {
            // 校验支持的通知类型
            checkNotifyType(type);
            int propertyId = sessionContext.getPropertyId(property);
            PropertyDTO propertyDTO = propertyI18nRedisService.get(propertyId);
            return emailConfigService.getEmailConfigDTO(propertyDTO, type);
        }

        /**
         * 保存邮件配置
         *
         * @param property       资产标识
         * @param emailConfigDTO 邮件配置信息
         * @return 邮件配置 id
         * @author zc
         * @date 2024/11/15
         */
        @ApiId(376)
        @PostMapping
        public Map<String, Integer> save(@RequestParam String property, @RequestBody @Valid SvaEmailConfigDTO emailConfigDTO) {
            int tenantId = sessionContext.getTenantSymbolBO().getId();
            int propertyId = sessionContext.getPropertyId(property);
            // 校验支持的通知类型
            checkNotifyType(emailConfigDTO.getType());
            // 校验是否不可关闭
            checkStatusClosed(emailConfigDTO.getType(), emailConfigDTO.getStatus());
            // 校验通知对象有效性
            checkEmailTarget(tenantId, propertyId, emailConfigDTO);
            return ImmutableMap.of("id", emailConfigService.save(propertyId, sessionContext.getLoginUserId(), emailConfigDTO));
        }

        /**
         * 校验支持的通知类型
         *
         * @param notifyType 报告类型
         * @author zc
         * @date 2026/3/6
         */
        private void checkNotifyType(int notifyType) {
            List<Integer> effectNotifyTypeList = List.of(SvaNotifyTypeEnum.DATA_PERMISSION_APPLY.getType(),
                    SvaNotifyTypeEnum.MISSING_REPORT_EMPLOYEE.getType(),
                    SvaNotifyTypeEnum.MISSING_REPORT_AV_ACTUAL.getType(),
                    SvaNotifyTypeEnum.MISSING_REPORT_OPERATING.getType(),
                    SvaNotifyTypeEnum.MISSING_DATA_SALES_FORECAST.getType(),
                    SvaNotifyTypeEnum.MISSING_REPORT_AV_SCHEDULE.getType());
            if (!effectNotifyTypeList.contains(notifyType)) {
                throw new BadRequestException(CommonErrorI18nConstant.NOTIFY_TYPE_NOT_SUPPORTED,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        MessageFormat.format("The currently selected notify type is not supported, " +
                                "notifyType must be in {0}", effectNotifyTypeList));
            }
        }

        /**
         * 校验是否不可关闭
         *
         * @param emailType 邮件类型
         * @param status    状态：true：开启，false：关闭
         * @author zc
         * @date 2025/1/22
         */
        private void checkStatusClosed(int emailType, boolean status) {
            List<Integer> cannotCloseTypeList = Lists.newArrayList(SvaNotifyTypeEnum.DATA_PERMISSION_APPLY.getType());
            if (!status && cannotCloseTypeList.contains(emailType)) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_CANNOT_CLOSE,
                        i18nMessageUtils.getErrorParamMapNotNeedTranslate("Data permission apply"),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The data permission type cannot be closed");
            }
        }

        /**
         * 校验通知对象有效性
         *
         * @param tenantId       租户 id
         * @param propertyId     资产 id
         * @param emailConfigDTO 邮件配置DTO
         * @author zc
         * @date 2024/11/12
         */
        private void checkEmailTarget(int tenantId, int propertyId, SvaEmailConfigDTO emailConfigDTO) {
            List<SvaEmailTargetDTO> allNotifyList = Lists.newArrayList();
            if (CollectionUtils.isNotEmpty(emailConfigDTO.getToList())) {
                allNotifyList.addAll(emailConfigDTO.getToList());
            }
            if (CollectionUtils.isNotEmpty(emailConfigDTO.getCcList())) {
                allNotifyList.addAll(emailConfigDTO.getCcList());
            }
            if (CollectionUtils.isEmpty(allNotifyList)) {
                return;
            }

            // 按照用户来源分组
            Map<Integer, List<Integer>> source2IdentityIdList = allNotifyList.stream()
                    .collect(Collectors.groupingBy(SvaEmailTargetDTO::getSource,
                            Collectors.mapping(SvaEmailTargetDTO::getIdentityId, Collectors.toList())));

            // 校验是否重复
            List<Integer> userIds = source2IdentityIdList.getOrDefault(EmailConfigCons.SOURCE_USER, Collections.emptyList());
            if (userIds.size() != Sets.newHashSet(userIds).size()) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_CANNOT_REPEAT,
                        i18nMessageUtils.getErrorParamMapNotNeedTranslate("userIds"),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The user cannot repeat");
            }
            List<Integer> userGroupIds = source2IdentityIdList.getOrDefault(EmailConfigCons.SOURCE_USERGROUP, Collections.emptyList());
            if (userGroupIds.size() != Sets.newHashSet(userGroupIds).size()) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_CANNOT_REPEAT,
                        i18nMessageUtils.getErrorParamMapNotNeedTranslate("userGroupIds"),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The user group cannot repeat");
            }
            List<String> manualEmails = allNotifyList.stream().filter(item -> item.getSource() == EmailConfigCons.SOURCE_MANUAL)
                    .map(SvaEmailTargetDTO::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(),
                        "The manual email cannot repeat");
            }
            // 用户集合
            if (CollectionUtils.isNotEmpty(userIds)) {
                List<UserDTO> allUsers = userInternalService.getUsers(tenantId, userIds);
                if (userIds.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 (userGroupIds.size() != userGroups.size()) {
                    throw new BadRequestException(CommonErrorI18nConstant.DATA_NOT_EXIST,
                            i18nMessageUtils.getErrorParamMapNotNeedTranslate("userGroupId"),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            "The user group not exist");
                }
            }
        }
    }