# ScheduleEmailConfigServiceImpl.java

    package cloud.wconnect.workperformance.service.schedule.impl;

    import cloud.wconnect.commonlib.component.BeanMapper;
    import cloud.wconnect.commonlib.cons.*;
    import cloud.wconnect.commonlib.exception.BadRequestException;
    import cloud.wconnect.commonlib.pojo.dto.NotifyDTO;
    import cloud.wconnect.commonlib.pojo.dto.NotifyIdentityDTO;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.pojo.dto.UserDTO;
    import cloud.wconnect.commonlib.pojo.dto.internationalization.InternationalizationDateTimeDTO;
    import cloud.wconnect.commonlib.pojo.dto.line.PublicFlexMsgParm;
    import cloud.wconnect.commonlib.redis.PropertyI18nRedisService;
    import cloud.wconnect.commonlib.service.NotifyInternalService;
    import cloud.wconnect.commonlib.service.UserInternalService;
    import cloud.wconnect.commonlib.service.UsergroupInternalService;
    import cloud.wconnect.commonlib.util.*;
    import cloud.wconnect.workperformance.cons.FunctionPointEnum;
    import cloud.wconnect.workperformance.cons.schedule.ScheduleLineNotifyTitleEnum;
    import cloud.wconnect.workperformance.cons.schedule.ScheduleNotifyEnum;
    import cloud.wconnect.workperformance.entity.schedule.ScheduleDepartment;
    import cloud.wconnect.workperformance.entity.schedule.ScheduleDepartmentPermissionConfig;
    import cloud.wconnect.workperformance.entity.schedule.ScheduleEmailConfig;
    import cloud.wconnect.workperformance.entity.schedule.ScheduleEmailTarget;
    import cloud.wconnect.workperformance.mapper.schedule.ScheduleEmailConfigMapper;
    import cloud.wconnect.workperformance.pojo.dto.schedule.ScheduleEmailConfigDTO;
    import cloud.wconnect.workperformance.pojo.dto.schedule.ScheduleEmailConfigTargetDTO;
    import cloud.wconnect.workperformance.pojo.query.schedule.ScheduleEmailQuery;
    import cloud.wconnect.workperformance.service.schedule.IScheduleDepartmentService;
    import cloud.wconnect.workperformance.service.schedule.IScheduleEmailConfigService;
    import cloud.wconnect.workperformance.service.schedule.IScheduleEmailTargetService;
    import com.alibaba.fastjson.JSONObject;
    import com.baomidou.dynamic.datasource.annotation.DS;
    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.collections.CollectionUtils;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Lazy;
    import org.springframework.context.i18n.LocaleContextHolder;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    import org.thymeleaf.TemplateEngine;
    import org.thymeleaf.context.Context;

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

    /**
     * <p>
     * schedule 邮件配置 服务实现类
     * </p>
     *
     * @author yyt
     * @since 2023-06-28
     */
    @Service
    @DS("work_performance")
    public class ScheduleEmailConfigServiceImpl extends ServiceImpl<ScheduleEmailConfigMapper, ScheduleEmailConfig> implements IScheduleEmailConfigService {

        @Autowired
        private IScheduleEmailTargetService scheduleEmailTargetService;
        @Lazy
        @Autowired
        private IScheduleDepartmentService scheduleDepartmentService;
        @Autowired
        private TemplateEngine templateEngine;
        @Autowired
        private UserInternalService userInternalService;
        @Autowired
        private PropertyI18nRedisService propertyI18nRedisService;
        @Autowired
        private UsergroupInternalService usergroupInternalService;
        @Autowired
        private BeanMapper beanMapper;
        @Autowired
        private LineMessageUtil lineMessageUtil;
        @Autowired
        private NotifyInternalService notifyInternalService;
        @Autowired
        private I18nMessageUtils i18nMessageUtils;

        @Value("${here.link.schedule}")
        private String hereLink;

        /**
         * 获取邮件配置
         *
         * @param propertyId 资产
         * @param type       类型（1：history and forecast 报告缺失，2：申请部门权限通知）
         * @param status     状态（false：关闭，true：开启）
         * @return 邮件配置
         * @author yyt
         * @date 2023/6/30
         */
        @Override
        public ScheduleEmailConfig getEmailConfig(Integer propertyId, int type, Boolean status) {
            return getOne(new QueryWrapper<ScheduleEmailConfig>().lambda()
                    .eq(ScheduleEmailConfig::getPropertyId, propertyId)
                    .eq(ScheduleEmailConfig::getType, type)
                    .eq(Objects.nonNull(status), ScheduleEmailConfig::getStatus, status));
        }

        /**
         * 发送申请权限配置邮件
         *
         * @param propertyDTO        资产DTO
         * @param scheduleDepartment 部门
         * @param write              true: 申请写权限, false: 申请读权限
         * @param userId             申请人
         * @author yyt
         * @date 2023/6/30
         */
        @Override
        public void sendApplyEmail(PropertyDTO propertyDTO, ScheduleDepartment scheduleDepartment, boolean write, int userId) {
            ScheduleEmailConfig scheduleEmailConfig = getEmailConfig(scheduleDepartment.getPropertyId(), ScheduleEmailConfig.TYPE_APPLY_PERMISSION_CONFIG, true);
            if (Objects.isNull(scheduleEmailConfig)) {
                return;
            }

            List<ScheduleEmailTarget> emailTargets = scheduleEmailTargetService.list(scheduleEmailConfig.getId());
            if (CollectionUtils.isEmpty(emailTargets)) return;

            Integer propertyId = propertyDTO.getId();
            String propertyName = propertyDTO.getName();
            String language = propertyDTO.getLanguage();
            InternationalizationDateTimeDTO dateTime = propertyDTO.getInternationalization().getDateTime();

            String finalHereLink = hereLink + "?property=" + propertyDTO.getAbbr() + "&tenant=" + propertyDTO.getTenantSymbol();
            String applyUserName = userInternalService.getOne(propertyDTO.getTenantId(), userId).getName();
            String functionName = i18nMessageUtils.message(FunctionCons.SCHEDULE, language);

            //获取line通知内容
            LinkedHashMap<String, String> businessMap = new LinkedHashMap<>();
            businessMap.put("Department", scheduleDepartment.getName());
            businessMap.put("Apply By", applyUserName);
            PublicFlexMsgParm publicFlexMsgParm = PublicFlexMsgParm.builder()
                    .propertyId(propertyId)
                    .propertyName(propertyName)
                    .functionName(functionName)
                    .title(ScheduleLineNotifyTitleEnum.APPLY_DEPARTMENT_PERMISSION.getI18nCode())
                    .link(finalHereLink)
                    .businessMap(businessMap)
                    .build();
            JSONObject lineContent = lineMessageUtil.generatePublicFlexMsg(publicFlexMsgParm);

            String defaultSubject = i18nMessageUtils.message(ScheduleNotifyEnum.APPLY_DEPARTMENT_PERMISSION.getI18nCode(), language,
                    i18nMessageUtils.message(FunctionCons.SCHEDULE, language), propertyName);

            NotifyDTO notifyDTO = new NotifyDTO();
            notifyDTO.setPropertyId(propertyId);
            notifyDTO.setFunctionPoint(FunctionPointEnum.SCHEDULE_PERMISSION.getFunctionPoint());
            notifyDTO.setSubject(StringUtils.isNotBlank(scheduleEmailConfig.getSubject()) ? scheduleEmailConfig.getSubject() : defaultSubject);
            notifyDTO.setIdentityList(beanMapper.mapList(emailTargets, NotifyIdentityDTO.class));

            //邮件内容
            Context context = new Context();
            context.setVariable("propertyName", propertyName);
            context.setVariable("functionName", functionName);
            context.setVariable("hereLink", finalHereLink);
            context.setVariable("approveStatus", i18nMessageUtils.message(DataPermissionStatusEnum.WAITING_FOR_APPROVAL.getI18nCode(), language));
            context.setVariable("permissionType", write ? i18nMessageUtils.message(CommonFieldI18nConstant.DATA_PERMISSION_READ_WRITE, language) :
                    i18nMessageUtils.message(CommonFieldI18nConstant.DATA_PERMISSION_READ_ONLY, language));
            context.setVariable("applyBy", applyUserName);
            context.setVariable("applyDate", InternationalizationUtils.formatDateTime(DateTimeUtils.nowTimestamp(), dateTime.getLongDate(),
                    null, propertyDTO.getTimezone(), language));
            context.setVariable("department", scheduleDepartment.getName());
            notifyDTO.setEmailContent(EmailUtil.getFullEmailContent(defaultSubject, templateEngine.process("schedule/schedule_data_permission_" + language, context), language));

            notifyDTO.setLineContent(Lists.newArrayList(lineContent));
            //通知
            notifyInternalService.sendNotify(notifyDTO);
        }

        /**
         * 发送权限允许通过邮件
         *
         * @param config      权限配置
         * @param propertyDTO 资产
         * @author yyt
         * @date 2023/7/3
         */
        @Override
        public void sendPassEmail(ScheduleDepartmentPermissionConfig config, PropertyDTO propertyDTO) {
            Integer propertyId = propertyDTO.getId();
            ScheduleEmailConfig emailConfig = getEmailConfig(propertyId, ScheduleEmailConfig.TYPE_APPLY_PERMISSION_CONFIG, true);
            if (Objects.isNull(emailConfig)) {
                return;
            }

            String propertyName = propertyDTO.getName();
            String language = propertyDTO.getLanguage();
            InternationalizationDateTimeDTO dateTime = propertyDTO.getInternationalization().getDateTime();

            String finalHereLink = hereLink + "?property=" + propertyDTO.getAbbr() + "&tenant=" + propertyDTO.getTenantSymbol();
            String functionName = i18nMessageUtils.message(FunctionCons.SCHEDULE, language);

            ScheduleDepartment department = scheduleDepartmentService.getById(config.getDepartmentId());
            //获取line通知内容
            LinkedHashMap<String, String> businessMap = new LinkedHashMap<>();
            businessMap.put("Department", department.getName());
            PublicFlexMsgParm publicFlexMsgParm = PublicFlexMsgParm.builder()
                    .propertyId(propertyId)
                    .propertyName(propertyName)
                    .functionName(functionName)
                    .title(ScheduleLineNotifyTitleEnum.PASS_DEPARTMENT_PERMISSION.getI18nCode())
                    .link(finalHereLink)
                    .businessMap(businessMap)
                    .build();
            JSONObject lineContent = lineMessageUtil.generatePublicFlexMsg(publicFlexMsgParm);

            String defaultSubject = i18nMessageUtils.message(NotifySubjectI18nCons.DATA_PERMISSION_APPROVAL, language,
                    i18nMessageUtils.message(FunctionCons.SCHEDULE, language), propertyName);

            NotifyDTO notifyDTO = new NotifyDTO();
            notifyDTO.setPropertyId(propertyId);
            notifyDTO.setFunctionPoint(FunctionPointEnum.SCHEDULE_PERMISSION.getFunctionPoint());
            notifyDTO.setSubject(defaultSubject);
            notifyDTO.setIdentityList(Lists.newArrayList(new NotifyIdentityDTO().setIdentityId(config.getTargetId())
                    .setSource(EmailConfigCons.SOURCE_USER).setTargetType(EmailConfigCons.TARGET_TYPE_TO).setEmail("")));

            // 邮件通知内容
            Context context = new Context();
            context.setVariable("propertyName", propertyName);
            context.setVariable("functionName", functionName);
            context.setVariable("hereLink", finalHereLink);
            context.setVariable("approveStatus", i18nMessageUtils.message(DataPermissionStatusEnum.getI18nCode(config.getStatus()), language));
            context.setVariable("permissionType", config.getPermissionType() == DataPermissionConfigConstant.PERMISSION_TYPE_READY ?
                    i18nMessageUtils.message(CommonFieldI18nConstant.DATA_PERMISSION_READ_ONLY, language) :
                    i18nMessageUtils.message(CommonFieldI18nConstant.DATA_PERMISSION_READ_WRITE, language));
            context.setVariable("applyBy", userInternalService.getOne(propertyDTO.getTenantId(), config.getTargetId()).getName());
            context.setVariable("applyDate", InternationalizationUtils.formatDateTime(config.getCreatedTime(), dateTime.getLongDate(),
                    null, propertyDTO.getTimezone(), language));
            context.setVariable("department", department.getName());
            notifyDTO.setEmailContent(EmailUtil.getFullEmailContent(defaultSubject, templateEngine.process("schedule/schedule_data_permission_" + language, context), language));

            notifyDTO.setLineContent(Lists.newArrayList(lineContent));
            //通知
            notifyInternalService.sendNotify(notifyDTO);
        }

        /**
         * 校验邮件配置存在
         *
         * @param id 邮件配置 id
         * @return 邮件配置
         * @author yyt
         * @date 2023/7/3
         */
        @Override
        public ScheduleEmailConfig checkExist(int id) {
            ScheduleEmailConfig emailConfig = getById(id);
            if (Objects.isNull(emailConfig)) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_NOT_FOUND,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "the scheduleEmailConfig is not found;id:" + id);
            }
            return emailConfig;
        }

        /**
         * 保存邮件通知
         *
         * @param propertyId     资产 id
         * @param emailConfigDTO 邮件配置
         * @param userId         操作人
         * @return 通知 id
         * @author yyt
         * @date 2023/7/3
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public Integer saveEmail(Integer propertyId, ScheduleEmailConfigDTO emailConfigDTO, int userId) {
            ScheduleEmailConfig config = beanMapper.map(emailConfigDTO, ScheduleEmailConfig.class);
            config.setPropertyId(propertyId).setUpdateBy(userId).setUpdateTime(DateTimeUtils.nowTimestamp());
            saveOrUpdate(config);

            if (emailConfigDTO.getId() > 0) {
                scheduleEmailTargetService.remove(new QueryWrapper<ScheduleEmailTarget>().lambda().eq(ScheduleEmailTarget::getConfigId,
                        emailConfigDTO.getId()));
            }

            List<ScheduleEmailTarget> targets = new ArrayList<>();
            if (CollectionUtils.isNotEmpty(emailConfigDTO.getToList())) {
                targets.addAll(emailConfigDTO.getToList().stream().map(item -> beanMapper.map(item, ScheduleEmailTarget.class,
                        target -> {
                            target.setTargetType(EmailConfigCons.TARGET_TYPE_TO);
                            target.setPropertyId(propertyId);
                            target.setConfigId(config.getId());
                        })).toList());
            }
            if (CollectionUtils.isNotEmpty(emailConfigDTO.getCcList())) {
                targets.addAll(emailConfigDTO.getCcList().stream().map(item -> beanMapper.map(item, ScheduleEmailTarget.class,
                        target -> {
                            target.setTargetType(EmailConfigCons.TARGET_TYPE_CC);
                            target.setPropertyId(propertyId);
                            target.setConfigId(config.getId());
                        })).toList());
            }
            if (CollectionUtils.isNotEmpty(targets)) {
                scheduleEmailTargetService.saveBatch(targets);
            }

            return config.getId();
        }

        /**
         * 校验存在
         *
         * @param propertyId 资产
         * @param type       类型（1：employee 报告缺失，2：申请部门权限通知, 3:history and forecast 报告缺失）
         * @author yyt
         * @date 2023/7/12
         */
        @Override
        public void checkExist(Integer propertyId, Integer type) {
            ScheduleEmailConfig config = getOne(new QueryWrapper<ScheduleEmailConfig>().lambda()
                    .eq(ScheduleEmailConfig::getPropertyId, propertyId)
                    .eq(ScheduleEmailConfig::getType, type));
            if (Objects.nonNull(config)) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_EXISTED,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "failed to save existing data");
            }
        }

        /**
         * 获取邮件通知默认主题
         *
         * @param propertyName 资产名称
         * @param type         类型（1：employee 报告缺失，2：申请部门权限通知, 3:history and forecast 报告缺失）
         * @param locale       多语言
         * @return 默认主题
         * @author yyt
         * @date 2024/11/21
         **/
        @Override
        public String getDefaultSubject(String propertyName, Integer type, Locale locale) {
            return i18nMessageUtils.message(ScheduleNotifyEnum.getI18nCode(type), locale,
                    i18nMessageUtils.message(FunctionCons.SCHEDULE, locale), propertyName);
        }

        /**
         * 获取邮件通知默认主题
         *
         * @param propertyId 资产 id
         * @param type       类型（1：employee 报告缺失，3：history and forecast 报告缺失）
         * @return 默认主题
         * @author yyt
         * @date 2024/11/18
         **/
        @Override
        public String getDefaultSubject(Integer propertyId, int type) {
            PropertyDTO propertyDTO = propertyI18nRedisService.get(propertyId);
            String language = propertyDTO.getLanguage();
            return i18nMessageUtils.message(NotifySubjectI18nCons.MISSING_REPORT, language,
                    i18nMessageUtils.message(FunctionCons.SCHEDULE, language), propertyDTO.getName());
        }

        /**
         * 获取邮件通知
         *
         * @param propertyId 资产 id
         * @param query      查询参数
         * @return 邮件通知DTO
         * @author yyt
         * @date 2023/7/12
         */
        @Override
        public ScheduleEmailConfigDTO getDTO(int propertyId, ScheduleEmailQuery query) {
            PropertyDTO propertyDTO = propertyI18nRedisService.get(propertyId);
            Locale locale = LocaleContextHolder.getLocale();
            String defaultSubject = getDefaultSubject(propertyDTO.getName(), query.getType(), locale);

            ScheduleEmailConfig config = getEmailConfig(propertyId, query.getType(), null);
            if (Objects.isNull(config)) {
                return new ScheduleEmailConfigDTO(defaultSubject);
            }

            List<ScheduleEmailTarget> emailTargets = scheduleEmailTargetService.list(config.getId());

            List<Integer> userIds = emailTargets.stream().filter(item -> item.getSource() == EmailConfigCons.SOURCE_USER)
                    .map(ScheduleEmailTarget::getIdentityId).distinct().toList();
            List<Integer> userGroupIds = emailTargets.stream().filter(item -> item.getSource() == EmailConfigCons.SOURCE_USERGROUP)
                    .map(ScheduleEmailTarget::getIdentityId).distinct().toList();

            Integer tenantId = propertyDTO.getTenantId();
            Map<Integer, UserDTO> userById = userInternalService.getUserMapByIds(tenantId, userIds);
            Map<Integer, String> userGroupNameById = usergroupInternalService.getUserGroupNameByIds(tenantId, propertyId, userGroupIds);
            emailTargets.forEach(item -> {
                if (item.getSource() == EmailConfigCons.SOURCE_USER) {
                    item.setIdentityName(userById.get(item.getIdentityId()).getName());
                    item.setEmail(userById.get(item.getIdentityId()).getEmail());
                }
                if (item.getSource() == EmailConfigCons.SOURCE_USERGROUP) {
                    item.setIdentityName(userGroupNameById.get(item.getIdentityId()));
                }
            });

            Map<Integer, List<ScheduleEmailTarget>> targetsByTargetType = emailTargets.stream()
                    .collect(Collectors.groupingBy(ScheduleEmailTarget::getTargetType));
            ScheduleEmailConfigDTO configDTO = beanMapper.map(config, ScheduleEmailConfigDTO.class);
            configDTO.setDefaultSubject(defaultSubject);
            configDTO.setToList(beanMapper.mapList(targetsByTargetType.get(EmailConfigCons.TARGET_TYPE_TO), ScheduleEmailConfigTargetDTO.class));
            configDTO.setCcList(beanMapper.mapList(targetsByTargetType.get(EmailConfigCons.TARGET_TYPE_CC), ScheduleEmailConfigTargetDTO.class));
            return configDTO;
        }

    }