package com.usky.pm.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.usky.common.core.bean.ApiResult;
import com.usky.common.core.exception.BusinessException;
import com.usky.common.mybatis.core.AbstractCrudService;
import com.usky.common.security.utils.SecurityUtils;
import com.usky.pm.domain.PmProject;
import com.usky.pm.domain.PmReceive;
import com.usky.pm.domain.PmWorkContent;
import com.usky.pm.domain.PmWorkReport;
import com.usky.pm.mapper.*;
import com.usky.pm.service.PmProjectService;
import com.usky.pm.service.PmWorkContentService;
import com.usky.pm.service.PmWorkReportService;
import com.usky.pm.service.config.DingTalkAndMessage;
import com.usky.pm.service.vo.PmProjectTotalWorkTimeVo;
import com.usky.pm.service.vo.PmProjectWorkTimeVo;
import com.usky.system.RemoteMceService;
import com.usky.system.domain.MceRequestVO;
import com.usky.system.domain.SysUser;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Year;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.stream.Collectors;


/**
 * <p>
 * 工作报告表 服务实现类
 * </p>
 *
 * @author fu
 * @since 2024-05-20
 */
@Slf4j
@Service
public class PmWorkReportServiceImpl extends AbstractCrudService<PmWorkReportMapper, PmWorkReport> implements PmWorkReportService {


    @Autowired
    private PmWorkContentMapper pmWorkContentMapper;

    @Autowired
    private PmWorkReportMapper pmWorkReportMapper;

    @Autowired
    private PmProjectService pmProjectService;

    @Autowired
    private PmWorkContentService pmWorkContentService;

    @Autowired
    private DingTalkAndMessage dingTalkAndMessage;

    @Autowired
    private PmReceiveMapper pmReceiveMapper;

    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private RemoteMceService remoteMceService;

    @Autowired
    private PmProjectMapper pmProjectMapper;

    private static final String INFO_TITLE = "日报未提交提醒";
    private static final String INFO_CONTENT = "您今天的日报还未提交，别忘记哦~";
    private static final int INFO_TYPE = 5;

    /**
     * 获取时间内工作报告
     *
     * @param startDate 开始时间
     * @param endDate   结束时间
     * @param reportId  报告id
     * @return
     */
    @Override
    public List<Map<String, List<PmWorkReport>>> weekWork(String startDate, String endDate, Integer reportId) {
        Long userId = SecurityUtils.getUserId();
        Integer tenantId = SecurityUtils.getTenantId();

        List<Map<String, List<PmWorkReport>>> returnList = new ArrayList<>();
        List<PmWorkReport> report = new ArrayList<>();
        Map<String, List<PmWorkReport>> weekData = new HashMap<>();
        Map<String, List<PmWorkReport>> newData = new HashMap<>();
        LocalDate startDate1 = null;
        LocalDate endDate1 = null;
        if (startDate == null && endDate == null) {
            startDate1 = LocalDate.now().with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
            endDate1 = LocalDate.now().with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY));
        } else {
            startDate1 = LocalDate.parse(startDate);
            endDate1 = LocalDate.parse(endDate);
        }
        List<LocalDate> dates = new ArrayList<>();
        LocalDate tempDate = startDate1;
        while (!tempDate.isAfter(endDate1)) {
            dates.add(tempDate);
            tempDate = tempDate.plusDays(1);
        }

        // 返回最新的一条数据给前端填充
        LambdaQueryWrapper<PmWorkReport> wrapper2 = Wrappers.lambdaQuery();
        wrapper2.eq(PmWorkReport::getSubmitterId, userId)
                .orderByDesc(PmWorkReport::getSubmitDate)
                .last("LIMIT 1");
        report = baseMapper.selectList(wrapper2);
        if (!report.isEmpty()) {
            Integer pId = report.get(0).getId();
            LambdaQueryWrapper<PmWorkContent> wrapper = Wrappers.lambdaQuery();
            wrapper.eq(PmWorkContent::getReportId, pId);
            List<PmWorkContent> contents1 = pmWorkContentMapper.selectList(wrapper);
            report.get(0).setWorkContents(contents1);
        }

        // 固定返回七条数据，没有内容也要设置时间给前端渲染
        LambdaQueryWrapper<PmWorkReport> queryWrapperR = Wrappers.lambdaQuery();
        queryWrapperR.eq(PmWorkReport::getTenantId, tenantId)
                .eq(PmWorkReport::getSubmitterId, userId)
                .between(reportId == 0, PmWorkReport::getReportDate, startDate1, endDate1)
                .eq(reportId != 0, PmWorkReport::getId, reportId)
                .orderByAsc(PmWorkReport::getReportDate);
        List<PmWorkReport> reports = baseMapper.selectList(queryWrapperR);
        if (reports.isEmpty()) {
            List<PmWorkReport> reports1 = new ArrayList<>();
            for (int f = 0; f < dates.size(); f++) {
                List<PmWorkContent> content = new ArrayList<>();
                PmWorkReport report1 = new PmWorkReport();
                report1.setReportDate(dates.get(f));
                report1.setWorkContents(content);
                reports1.add(report1);
            }
            weekData.put("weekData", reports1);
            newData.put("newData", report);
            returnList.add(0, newData);
            returnList.add(1, weekData);
            return returnList;
        }
        List<Integer> ids = new ArrayList<>();
        for (PmWorkReport pwr : reports) {
            ids.add(pwr.getId());
        }
        LambdaQueryWrapper<PmWorkContent> queryWrapperC = Wrappers.lambdaQuery();
        queryWrapperC.select(PmWorkContent::getId, PmWorkContent::getWorkContent, PmWorkContent::getWorkTime, PmWorkContent::getProjectId,
                        PmWorkContent::getProjectName, PmWorkContent::getReportId, PmWorkContent::getCreateTime, PmWorkContent::getUpdateTime)
                .in(PmWorkContent::getReportId, ids)
                .orderByDesc(PmWorkContent::getWorkTime);
        List<PmWorkContent> contents = pmWorkContentMapper.selectList(queryWrapperC);
        int a, b;
        for (int i = 0; i < reports.size(); i++) {
            a = reports.get(i).getId();
            List<PmWorkContent> contentList = new ArrayList<>();
            for (int j = 0; j < contents.size(); j++) {
                b = contents.get(j).getReportId();
                if (b == a) {
                    contentList.add(contents.get(j));
                }
            }
            reports.get(i).setWorkContents(contentList);
        }
        if (reports.size() == 7 || reportId != 0) {
            weekData.put("weekData", reports);
            LambdaQueryWrapper<PmWorkReport> wrapper1 = Wrappers.lambdaQuery();
            wrapper1.eq(PmWorkReport::getSubmitterId, userId)
                    .orderByDesc(PmWorkReport::getReportDate)
                    .last("LIMIT 1");
            report = this.list(wrapper1);
            Integer rId = report.get(0).getId();
            LambdaQueryWrapper<PmWorkContent> wrapperC = Wrappers.lambdaQuery();
            wrapperC.eq(PmWorkContent::getProjectId, rId);
            List<PmWorkContent> pmWorkContents = pmWorkContentMapper.selectList(wrapperC);
            report.get(0).setWorkContents(pmWorkContents);
            newData.put("newData", report);
            returnList.add(0, newData);
            returnList.add(1, weekData);
            return returnList;
        }
        List<PmWorkReport> reportList = new ArrayList<>();
        for (int d = 0; d < dates.size(); d++) {
            boolean matchFound = false;
            for (int c = 0; c < reports.size(); c++) {
                if (dates.get(d).isEqual(reports.get(c).getReportDate())) {
                    reportList.add(reports.get(c));
                    matchFound = true;
                    break;
                }
            }
            if (!matchFound) {
                PmWorkReport newReport = new PmWorkReport();
                List<PmWorkContent> newContent = new ArrayList<>();
                newReport.setReportDate(dates.get(d));
                newReport.setWorkContents(newContent);
                reportList.add(newReport);
            }
        }
        weekData.put("weekData", reportList);
        newData.put("newData", report);
        returnList.add(0, newData);
        returnList.add(1, weekData);
        return returnList;
    }

    /**
     * 新增、编辑工作报告
     */
    @Transactional
    @Override
    public void addReport(PmWorkReport pmWorkReport) {
        Long deptId = SecurityUtils.getLoginUser().getSysUser().getDeptId();
        String userName = SecurityUtils.getUsername();
        Long userId = SecurityUtils.getUserId();
        Integer tenantId = SecurityUtils.getTenantId();
        BigDecimal totalWorkTime = BigDecimal.ZERO;
        LocalDateTime dateTime = LocalDateTime.now();
        LocalDateTime timingTime = pmWorkReport.getTimingTime();
        LocalDateTime currentTimePlusMinutes = dateTime.plusMinutes(5);
        String ccTo = pmWorkReport.getCcTo();
        Integer rid = pmWorkReport.getId();
        List<PmWorkContent> contents = pmWorkReport.getWorkContents();

        List<PmWorkContent> workContents = pmWorkReport.getWorkContents();
        if (workContents == null) {
            throw new BusinessException("报告内容不能为空，请检查后提交！");
        } else {

            // 查询租户下所有项目
            LambdaQueryWrapper<PmProject> queryWrapper = Wrappers.lambdaQuery();
            queryWrapper.select(PmProject::getId, PmProject::getProjectName)
                    .eq(PmProject::getTenantId, tenantId)
                    .eq(PmProject::getDelFlag, 0);
            Map<Integer, String> projectMap = pmProjectMapper.selectList(queryWrapper).stream()
                    .collect(Collectors.toMap(PmProject::getId, PmProject::getProjectName));

            for (PmWorkContent workContent : workContents) {
                BigDecimal workTime = workContent.getWorkTime();
                // 单个工时大于零
                if (workTime == null) {
                    throw new BusinessException("工时不能为空，请检查后提交！");
                } else if (workTime.compareTo(BigDecimal.ZERO) <= 0) {
                    throw new BusinessException("工时必须大于零！");
                } else if (workTime.scale() > 1) {
                    throw new BusinessException("工时小数位超出长度，请重新输入！");
                } else if (workTime.precision() - workTime.scale() > 3) {
                    throw new BusinessException("工时整数位超出长度，请重新输入！");
                }

                String workContent1 = workContent.getWorkContent();
                // 工作内容不允许为空或空字符串
                if (StringUtils.isBlank(workContent1)) {
                    throw new BusinessException("工作内容不能为空，请检查后提交！");
                }

                // 检查项目ID是否有效
                if (workContent.getProjectId() == null || workContent.getProjectId() <= 0) {
                    throw new BusinessException("项目ID无效，请选择正确项目后提交。");
                }

                // 检查项目名称是否为空
                if (StringUtils.isBlank(workContent.getProjectName())) {
                    throw new BusinessException("项目名称不能为空，请检查后提交。");
                }

                // 检查项目ID和项目名称是否匹配
                if (!projectMap.containsKey(workContent.getProjectId()) ||
                        !projectMap.get(workContent.getProjectId()).equals(workContent.getProjectName())) {
                    throw new BusinessException("项目不存在或项目名称不匹配，请检查后提交。");
                }
            }
        }
        Set<Integer> projectIds2 = new HashSet<>();
        for (PmWorkContent workContent : pmWorkReport.getWorkContents()) {
            if (!projectIds2.add(workContent.getProjectId())) {
                throw new BusinessException("存在重复项目，请检查后提交！");
            }
        }
        for (PmWorkContent a : pmWorkReport.getWorkContents()) {
            totalWorkTime = totalWorkTime.add(a.getWorkTime());
        }
        // 判断总工时是否超过24h
        BigDecimal maxWorkTimePerDay = BigDecimal.valueOf(24);
        if (totalWorkTime.compareTo(maxWorkTimePerDay) >= 0) {
            throw new BusinessException("总工时超过24小时，请检查后提交！");
        }

        // 判断是否携带reportId，不写带则为新增报告，携带则为编辑（更新）报告
        if (rid == null) {
            // 判断是否今天已提交过
            LambdaQueryWrapper<PmWorkReport> wrapper = Wrappers.lambdaQuery();
            wrapper.eq(PmWorkReport::getReportDate, pmWorkReport.getReportDate())
                    .eq(PmWorkReport::getSubmitterId, userId)
                    .eq(PmWorkReport::getTenantId, tenantId);
            if (!pmWorkReportMapper.selectList(wrapper).isEmpty()) {
                throw new BusinessException("今天已提交工作报告，请勿重复提交！");
            }

            PmWorkReport newReport = new PmWorkReport();
            newReport.setReportDate(pmWorkReport.getReportDate());
            newReport.setSubmitterId(userId);
            newReport.setSubmitDate(LocalDateTime.now());
            newReport.setCcTo(ccTo);
            newReport.setCoordinateWork(pmWorkReport.getCoordinateWork());
            newReport.setTomorrowPlan(pmWorkReport.getTomorrowPlan());
            newReport.setCreateBy(userName);
            newReport.setCreateTime(dateTime);
            newReport.setDeptId(deptId);
            newReport.setTenantId(tenantId);
            newReport.setTotalHours(totalWorkTime);
            newReport.setSendDingTalk(pmWorkReport.getSendDingTalk());
            newReport.setReportFile(pmWorkReport.getReportFile());
            newReport.setReportImage(pmWorkReport.getReportImage());
            newReport.setReportStatus(1);
            if (pmWorkReport.getIsRegularlySend() == 1 && timingTime != null) {
                if (timingTime.isAfter(currentTimePlusMinutes)) {
                    newReport.setIsRegularlySend(pmWorkReport.getIsRegularlySend());
                    newReport.setTimingTime(timingTime);
                    newReport.setReportStatus(0);
                } else {
                    throw new BusinessException("定时发送支持选中5分钟后的时间，请重新选择时间");
                }
            }
            try {
                pmWorkReportMapper.insert(newReport);

            } catch (Exception e) {
                throw new BusinessException("新增报告异常！请联系管理员" + e);
            }

            // 获取报告中所有项目id
            List<Integer> projectIds = new ArrayList<>();
            for (PmWorkContent b1 : pmWorkReport.getWorkContents()) {
                projectIds.add(b1.getProjectId());
            }
            // 查出所有项目id对应项目名
            List<PmProject> project = pmProjectService.projectName(projectIds);
            // 将项目名重新赋值
            List<PmWorkContent> pmWorkContents = new ArrayList<>();
            for (PmWorkContent b : pmWorkReport.getWorkContents()) {
                for (int c = 0; c < project.size(); c++) {
                    if (b.getProjectId().equals(project.get(c).getId())) {
                        b.setProjectName(project.get(c).getProjectName());
                    }
                }
                PmWorkContent newContent = new PmWorkContent();
                newContent.setReportId(newReport.getId());
                newContent.setProjectId(b.getProjectId());
                projectIds.add(b.getReportId());
                newContent.setProjectName(b.getProjectName());
                newContent.setSubmitterId(userId);
                newContent.setWorkContent(b.getWorkContent());
                newContent.setWorkTime(b.getWorkTime());
                newContent.setCreateBy(userName);
                newContent.setCreateTime(dateTime);
                newContent.setDeptId(deptId);
                newContent.setTenantId(tenantId);
                pmWorkContentMapper.insert(newContent);
                pmWorkContents.add(newContent);
            }

            // 抄送人不为空、非定时发送
            if (timingTime == null) {
                if (StringUtils.isNotEmpty(ccTo)) {
                    // 推送消息中心
                    dingTalkAndMessage.sendAsyncMessage(newReport);
                    // 存入报告消息表
                    receiveMessages(ccTo, newReport.getId());
                }
                // 是否同步钉钉
                if (pmWorkReport.getSendDingTalk().equals(1)) {
/*                    String dingTalkUserId = dingTalkAndMessage.getDingTalkUserId(userId);
                    if (StringUtils.isBlank(dingTalkUserId)){
                        throw new BusinessException("注册手机号与钉钉手机号不一致，无法抄送钉钉");
                    }*/
                    dingTalkAndMessage.sendDingTalkDailyReport(newReport, pmWorkContents);
                }
            }
        } else {
            PmWorkReport rp = new PmWorkReport();
            LambdaQueryWrapper<PmWorkContent> queryWrapper = Wrappers.lambdaQuery();
            queryWrapper.select(PmWorkContent::getReportId, PmWorkContent::getSubmitterId, PmWorkContent::getCreateBy, PmWorkContent::getCreateTime, PmWorkContent::getDeptId, PmWorkContent::getTenantId)
                    .eq(PmWorkContent::getReportId, rid)
                    .last("LIMIT 1");
            PmWorkContent f = pmWorkContentMapper.selectOne(queryWrapper);

            if (pmWorkReport.getReportStatus().equals(0)) {
                if (timingTime == null) {
                    rp.setTimingTime(timingTime);
                } else if (timingTime.isAfter(currentTimePlusMinutes)) {
                    rp.setTimingTime(timingTime);
                } else {
                    throw new BusinessException("定时发送支持选中5分钟后的时间，请重新选择时间！");
                }
                rp.setId(rid);
                rp.setSubmitterId(pmWorkReport.getSubmitterId());
                rp.setCcTo(ccTo);
                rp.setSubmitDate(LocalDateTime.now());
                rp.setCoordinateWork(pmWorkReport.getCoordinateWork());
                rp.setTomorrowPlan(pmWorkReport.getTomorrowPlan());
                rp.setCreateBy(pmWorkReport.getCreateBy());
                rp.setUpdateBy(userName);
                rp.setUpdateTime(dateTime);
                rp.setTotalHours(totalWorkTime);
                rp.setIsRegularlySend(pmWorkReport.getIsRegularlySend());
                rp.setReportFile(pmWorkReport.getReportFile());
                rp.setReportImage(pmWorkReport.getReportImage());
                rp.setSendDingTalk(pmWorkReport.getSendDingTalk());
                pmWorkReportMapper.updateById(rp);

                pmWorkContentService.deleteContent(rid);
                List<PmWorkContent> contentsList = new ArrayList<>();
                for (PmWorkContent e : contents) {
                    e.setReportId(f.getReportId());
                    e.setUpdateBy(userName);
                    e.setUpdateTime(dateTime);
                    e.setSubmitterId(f.getSubmitterId());
                    e.setCreateTime(f.getCreateTime());
                    e.setCreateBy(f.getCreateBy());
                    e.setDeptId(f.getDeptId());
                    e.setTenantId(f.getTenantId());
                    pmWorkContentMapper.insert(e);
                    contentsList.add(e);
                }

                if (pmWorkReport.getIsRegularlySend().equals(0)) {
                    // 检查是否需要发送消息
                    if (StringUtils.isNotBlank(ccTo)) {
                        if (pmWorkReport.getSendDingTalk().equals(1)) {
                            dingTalkAndMessage.sendDingTalkDailyReport(rp, contents);
                        }
                        rp.setTenantId(tenantId);
                        dingTalkAndMessage.sendAsyncMessage(rp);
                        receiveMessages(ccTo, rid);
                    }
                    // 更新报告状态
                    rp.setReportStatus(1);
                    pmWorkReportMapper.updateById(rp);
                }
            } else {
                rp.setId(rid);
                rp.setCoordinateWork(pmWorkReport.getCoordinateWork());
                rp.setTomorrowPlan(pmWorkReport.getTomorrowPlan());
                rp.setUpdateBy(userName);
                rp.setUpdateTime(dateTime);
                rp.setTotalHours(totalWorkTime);
                rp.setIsRegularlySend(pmWorkReport.getIsRegularlySend());
                rp.setReportFile(pmWorkReport.getReportFile());
                rp.setReportImage(pmWorkReport.getReportImage());
                pmWorkReportMapper.updateById(rp);

                pmWorkContentService.deleteContent(rid);
                List<PmWorkContent> contentsList = new ArrayList<>();
                for (PmWorkContent e : contents) {
                    e.setReportId(f.getReportId());
                    e.setUpdateBy(userName);
                    e.setUpdateTime(dateTime);
                    e.setSubmitterId(f.getSubmitterId());
                    e.setCreateTime(f.getCreateTime());
                    e.setCreateBy(f.getCreateBy());
                    e.setDeptId(f.getDeptId());
                    e.setTenantId(f.getTenantId());
                    pmWorkContentMapper.insert(e);
                    contentsList.add(e);
                }
            }
        }
    }

    private void receiveMessages(String ids, Integer reportId) {
        if (StringUtils.isBlank(ids)) {
            return;
        }
        List<Long> longList = Arrays.stream(ids.split(","))
                .map(Long::parseLong)
                .collect(Collectors.toList());
        Map<Long, String> users = pmWorkContentService.nickNames(longList).stream().collect(Collectors.toMap(SysUser::getUserId, SysUser::getUserName));

        LambdaQueryWrapper<PmWorkReport> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.select(PmWorkReport::getCreateBy)
                .eq(PmWorkReport::getId, reportId);
        PmWorkReport pmWorkReport = pmWorkReportMapper.selectOne(queryWrapper);
        String createBy = pmWorkReport.getCreateBy();
        Long deptId = pmWorkReport.getDeptId();
        Integer tenantId = pmWorkReport.getTenantId();

        if (!longList.isEmpty()) {
            for (Long id : longList) {
                PmReceive pmReceive = new PmReceive();
                pmReceive.setReceiverId(id);
                pmReceive.setReceiverName(users.get(id));
                pmReceive.setReportId(reportId);
                pmReceive.setCreateBy(createBy);
                pmReceive.setCreateTime(LocalDateTime.now());
                pmReceive.setDeptId(deptId);
                pmReceive.setTenantId(tenantId);
                pmReceive.setReadFlag(0);
                try {
                    pmReceiveMapper.insert(pmReceive);
                } catch (Exception e) {
                    log.error("报告接收人：{}，报告发送人：{}，存入报告消息表失败！", id, createBy, e);
                    throw new BusinessException(createBy + " 的工作报告存入报告消息表失败！");
                }
            }
        }
    }

    @Override
    public PmProjectTotalWorkTimeVo countTime(Integer nowDate, Integer dateType, Integer dateNum) {
        int scale = 4;
        RoundingMode roundingMode = RoundingMode.HALF_UP;
        PmProjectTotalWorkTimeVo projectTotalWorkTimeVo = new PmProjectTotalWorkTimeVo();
        Map<String, LocalDateTime> stringLocalDateTimeMap = countWeekOrMonth(nowDate, dateType, dateNum);
        LocalDateTime startTime = stringLocalDateTimeMap.get("start");
        LocalDateTime endTime = stringLocalDateTimeMap.get("end");
        BigDecimal totalWorkTime = BigDecimal.ZERO;

        List<PmProjectWorkTimeVo> week = pmWorkContentMapper.workTimeCount(startTime, endTime, SecurityUtils.getUserId(), SecurityUtils.getTenantId());
        if (week.isEmpty()) {
            projectTotalWorkTimeVo.setTotalWorkTime(totalWorkTime);
            projectTotalWorkTimeVo.setProjectWorkTime(week);
            return projectTotalWorkTimeVo;
        }
        totalWorkTime = week.stream()
                .map(PmProjectWorkTimeVo::getWorkTime)
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        List<PmProjectWorkTimeVo> projectWorkTimeWithPercentage = calculatePercentage(week, totalWorkTime, scale, roundingMode);
        projectTotalWorkTimeVo.setTotalWorkTime(totalWorkTime);
        projectTotalWorkTimeVo.setProjectWorkTime(projectWorkTimeWithPercentage);

        return projectTotalWorkTimeVo;
    }

    // 计算百分比
    private List<PmProjectWorkTimeVo> calculatePercentage(List<PmProjectWorkTimeVo> workTimeList, BigDecimal totalWorkTime, int scale, RoundingMode roundingMode) {
        BigDecimal totalPercentage = BigDecimal.ZERO;
        List<PmProjectWorkTimeVo> projectWorkTimeWithPercentage = new ArrayList<>();

        for (int i = 0; i < workTimeList.size(); i++) {
            PmProjectWorkTimeVo workTimeVo = workTimeList.get(i);
            BigDecimal percentage = workTimeVo.getWorkTime().divide(totalWorkTime, scale, roundingMode);
            totalPercentage = totalPercentage.add(percentage);

            if (totalPercentage.compareTo(BigDecimal.ONE) > 0) {
                percentage = BigDecimal.ONE.subtract(totalPercentage.subtract(percentage));
            }

            workTimeVo.setPercentage(percentage);
            projectWorkTimeWithPercentage.add(workTimeVo);
        }

        return projectWorkTimeWithPercentage;
    }

    /**
     * @description: 计算周和月
     * @author: fu
     * @date: 2024/8/7 20:17
     * @param: [dateType, dateNum]
     * @return: java.util.Map<java.lang.String, java.time.LocalDateTime>
     **/
    public Map<String, LocalDateTime> countWeekOrMonth(Integer nowYear, Integer dateType, Integer dateNum) {
        Map<String, LocalDateTime> timePeriod = new HashMap<>();
        Year currentYear = Year.now();
        if (nowYear != null && nowYear > 0) {
            currentYear = Year.of(nowYear);
        }

        if (dateType == 1) {
            LocalDate lastDayOfYear = currentYear.atDay(currentYear.length());
            LocalDate lastMondayOfYear = lastDayOfYear.with(TemporalAdjusters.previousOrSame(java.time.DayOfWeek.MONDAY));
            long totalWeeks = lastMondayOfYear.getDayOfYear() / 7 + 1;
            if (dateNum > totalWeeks) {
                throw new BusinessException("周数错误！请重新选择");
            }

            int yearValue = currentYear.getValue();
            LocalDate firstDayOfYear = LocalDate.of(yearValue, 1, 1);
            LocalDate firstMondayOfYear = firstDayOfYear.with(TemporalAdjusters.nextOrSame(DayOfWeek.MONDAY));

            DayOfWeek dayOfWeek = firstDayOfYear.getDayOfWeek();
            if (dayOfWeek != DayOfWeek.MONDAY) {
                dateNum -= 1;
            }

            LocalDate startOfWeek = firstMondayOfYear.plusWeeks(dateNum - 1);
            LocalDate endOfWeek = startOfWeek.plusWeeks(1).minusDays(1);
            LocalDateTime startTimeOfWeek = startOfWeek.atStartOfDay();
            LocalDateTime endTimeOfWeek = endOfWeek.atTime(23, 59, 59);
            timePeriod.put("start", startTimeOfWeek);
            timePeriod.put("end", endTimeOfWeek);

        } else if (dateType == 2) {
            if (dateNum < 1 || dateNum > 12) {
                throw new BusinessException("月数错误！请重新选择");
            }
            LocalDate startOfMonth = currentYear.atMonth(dateNum).atDay(1);
            LocalDate endOfMonth = startOfMonth.with(TemporalAdjusters.lastDayOfMonth());
            LocalDateTime startTimeOfMonth = startOfMonth.atStartOfDay();
            LocalDateTime endTimeOfMonth = endOfMonth.atTime(23, 59, 59);
            timePeriod.put("start", startTimeOfMonth);
            timePeriod.put("end", endTimeOfMonth);

        } else {
            throw new BusinessException("类型选择错误！请选择周或月");
        }

        return timePeriod;
    }


    /**
     * @description: 工作报告定时发送
     * @author: fu
     * @date: 2024/8/7 20:17
     * @param: [time] 开始时间
     * @return: void
     **/
    @Async
    @Override
    public void timedSending(LocalDateTime time) {
        log.info("定时发送报告任务开始---------------------------");
        LambdaQueryWrapper<PmWorkReport> reports = Wrappers.lambdaQuery();
        reports.eq(PmWorkReport::getReportStatus, 0)
                .eq(PmWorkReport::getIsRegularlySend, 1)
                .le(PmWorkReport::getTimingTime, time)
                .orderByAsc(PmWorkReport::getTimingTime);
        List<PmWorkReport> reportList = pmWorkReportMapper.selectList(reports);
        if (!reportList.isEmpty()) {
            List<Integer> reportIds = new ArrayList<>();
            for (PmWorkReport report : reportList) {
                reportIds.add(report.getId());
            }
            LambdaQueryWrapper<PmWorkContent> reportContents = Wrappers.lambdaQuery();
            reportContents.in(PmWorkContent::getReportId, reportIds);
            List<PmWorkContent> pmWorkContentList = pmWorkContentMapper.selectList(reportContents);
            for (PmWorkReport report : reportList) {
                log.info("报告内容：{}", report);
                Integer id = report.getId();
                List<PmWorkContent> workContents = new ArrayList<>();
                for (PmWorkContent content : pmWorkContentList) {
                    Integer reportId = content.getReportId();
                    if (reportId.equals(id)) {
                        workContents.add(content);
                    }
                }
                try {
                    // 推送消息中心
                    log.info("定时报告：" + report.getId() + "调用消息中心开始，时间：" + LocalDateTime.now());
                    dingTalkAndMessage.sendAsyncMessage(report);
                    log.info("定时报告：" + report.getId() + "调用消息中心结束，时间" + LocalDateTime.now());
                } catch (Exception e) {
                    log.error("定时报告" + report.getId() + "调用消息中心异常" + e);
                }
                if (report.getSendDingTalk() == 1) {
                    try {
                        dingTalkAndMessage.sendDingTalkDailyReport(report, workContents);
                    } catch (Exception e) {
                        log.error("定时报告" + report.getId() + "发送钉钉异常" + e);
                    }
                }

                try {
                    report.setReportStatus(1);
                    report.setSubmitDate(time);
                    pmWorkReportMapper.updateById(report);
                    receiveMessages(report.getCcTo(), report.getId());
                } catch (Exception e) {
                    log.error("定时报告" + report.getId() + "更新状态异常" + e);
                    report.setReportStatus(1);
                    report.setSubmitDate(time);
                    pmWorkReportMapper.updateById(report);
                }
            }
        } else {
            log.info("当前没有定时的工作报告可发送");
        }
        log.info("定时发送报告任务结束---------------------------");
    }

    /**
     * @description: 删除报告
     * @author: fu
     * @date: 2024/8/7 20:17
     * @param: [reportId]
     * @return: void
     **/
    @Transactional
    @Override
    public void deleteContent(Integer reportId) {
        if (reportId == null || reportId < 0) {
            throw new BusinessException("报告id不正确，删除报告失败！");
        }

        Long userId = SecurityUtils.getUserId();
        LambdaQueryWrapper<PmWorkReport> report = Wrappers.lambdaQuery();
        report.select(PmWorkReport::getId)
                .eq(PmWorkReport::getSubmitterId, userId)
                .eq(PmWorkReport::getId, reportId);
        PmWorkReport pmWorkReport = pmWorkReportMapper.selectOne(report);
        if (pmWorkReport == null) {
            throw new BusinessException("您没有删除该报告权限！");
        }

        LambdaQueryWrapper<PmWorkContent> reportContents = Wrappers.lambdaQuery();
        reportContents.eq(PmWorkContent::getReportId, reportId);
        pmWorkContentMapper.delete(reportContents);

        try {
            pmWorkReportMapper.deleteById(reportId);
        } catch (Exception e) {
            log.error("删除报告失败！" + e);
            throw new BusinessException("删除报告失败请联系管理员");
        }
    }

    /**
     * @description: 查询定时报告
     * @author: fu
     * @date: 2024/8/7 20:17
     * @param: []
     * @return: java.util.List<com.usky.pm.domain.PmWorkReport>
     **/
    @Override
    public List<PmWorkReport> timedReportQuery() {
        Long userId = SecurityUtils.getUserId();
        LambdaQueryWrapper<PmWorkReport> reports = Wrappers.lambdaQuery();
        reports.eq(PmWorkReport::getSubmitterId, userId)
                .eq(PmWorkReport::getReportStatus, 0)
                .orderByAsc(PmWorkReport::getReportDate);
        List<PmWorkReport> timedReports = pmWorkReportMapper.selectList(reports);

        if (timedReports.isEmpty()) {
            return timedReports;
        }

        List<Integer> reportIds = timedReports.stream().map(PmWorkReport::getId).collect(Collectors.toList());

        List<Long> userIds = timedReports.stream().map(PmWorkReport::getSubmitterId).collect(Collectors.toList());
        List<SysUser> sysUsers = pmWorkContentService.nickNames(userIds);
        Map<Long, String> userNicknameMap = sysUsers.stream().collect(Collectors.toMap(SysUser::getUserId, SysUser::getNickName));

        LambdaQueryWrapper<PmWorkContent> reportContents = Wrappers.lambdaQuery();
        reportContents.in(PmWorkContent::getReportId, reportIds);
        List<PmWorkContent> pmWorkContentList = pmWorkContentMapper.selectList(reportContents);
        for (PmWorkReport report : timedReports) {
            Integer id = report.getId();
            List<PmWorkContent> workContents = new ArrayList<>();
            for (PmWorkContent content : pmWorkContentList) {
                Integer reportId = content.getReportId();
                if (reportId.equals(id)) {
                    workContents.add(content);
                }
            }
            report.setWorkContents(workContents);
            report.setCreateBy(userNicknameMap.get(report.getSubmitterId()));
        }
        return timedReports;
    }

    /**
     * @description: 报告提交提醒
     * @author: fu
     * @date: 2024/8/7 20:17
     * @param: []
     * @return: void
     **/
    @Async
    @Override
    public void reportSubmissionReminder() {
        Integer tenantId = SecurityUtils.getTenantId();
        List<Long> userIds = new ArrayList<>();
        LambdaQueryWrapper<PmWorkReport> todayReports = Wrappers.lambdaQuery();
        todayReports.eq(PmWorkReport::getReportStatus, 1)
                .eq(PmWorkReport::getReportDate, LocalDate.now())
                .eq(PmWorkReport::getTenantId, tenantId);
        List<PmWorkReport> todayReportsList = pmWorkReportMapper.selectList(todayReports);
        if (!todayReportsList.isEmpty()) {
            userIds = todayReportsList.stream().map(PmWorkReport::getSubmitterId).collect(Collectors.toList());
        }

        LambdaQueryWrapper<SysUser> userQueryWrapper = Wrappers.lambdaQuery();
        userQueryWrapper.select(SysUser::getUserId, SysUser::getNickName)
                .eq(SysUser::getTenantId, tenantId)
                .eq(SysUser::getStatus, 0)
                .eq(SysUser::getDelFlag, 0)
                .notIn(!userIds.isEmpty(), SysUser::getUserId, userIds);
        userIds = sysUserMapper.selectList(userQueryWrapper).stream().map(SysUser::getUserId).collect(Collectors.toList());

        MceRequestVO mceRequestVO = new MceRequestVO();
        mceRequestVO.setInfoTitle(INFO_TITLE);
        mceRequestVO.setInfoContent(INFO_CONTENT);
        mceRequestVO.setInfoType(String.valueOf(INFO_TYPE));
        mceRequestVO.setId(0);
        mceRequestVO.setInfoTypeName(INFO_TITLE);
        mceRequestVO.setUserName("报告管理系统");
        mceRequestVO.setTenantId(tenantId);
        if (!userIds.isEmpty()) {
            mceRequestVO.setUserIds(userIds);
        }
        try {
            // 推送消息中心
            remoteMceService.addMceReceive(mceRequestVO);
        } catch (Exception e) {
            log.error("报告提醒通知推送消息中心异常" + e);
        }
    }
}
