package jnpf.notice.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageInfo;
import jnpf.base.UserInfo;
import jnpf.cultivate.utils.UserApiV2Util;
import jnpf.model.cultivate.dto.learn.BatchCommonCountDto;
import jnpf.model.cultivate.vo.common.InnerPowerUserVO;
import jnpf.model.notice.domain.FtbNoticeAnnouncements;
import jnpf.model.notice.domain.FtbNoticeAnnouncementsLog;
import jnpf.model.notice.domain.FtbNoticeAnnouncementsReceive;
import jnpf.model.notice.domain.FtbNoticeCategories;
import jnpf.model.notice.dto.*;
import jnpf.model.notice.enums.NoticeEnums;
import jnpf.model.notice.req.announcement.*;
import jnpf.notice.mapper.FtbNoticeAnnouncementsMapper;
import jnpf.notice.service.FtbNoticeAnnouncementsReceiveService;
import jnpf.notice.service.FtbNoticeAnnouncementsService;
import jnpf.notice.service.FtbNoticeCategoriesService;
import jnpf.notice.service.FtbNoticeFilesService;
import jnpf.notice.utils.NoticeAsyncDealUtil;
import jnpf.notice.utils.NoticeUtils;
import jnpf.permission.vo.v2.user.UserBoundVO;
import jnpf.util.UploaderUtil;
import jnpf.util.UserProvider;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * @author 许贵林
 * @description 针对表【ftb_notice_announcements(公告表)】的数据库操作Service实现
 * @createDate 2024-05-08 09:42:09
 */
@Service
@Slf4j
public class FtbNoticeAnnouncementsServiceImpl extends ServiceImpl<FtbNoticeAnnouncementsMapper, FtbNoticeAnnouncements> implements FtbNoticeAnnouncementsService {
    private final static Integer CONTENT_CUT_MAX_LENGTH = 250;//公告内容shortContent最大长度

    @Autowired
    private FtbNoticeAnnouncementsReceiveService ftbNoticeAnnouncementsReceiveService;


    @Autowired
    private FtbNoticeFilesService ftbNoticeFilesService;

    @Autowired
    private NoticeUtils noticeUtils;

    @Autowired
    private NoticeAsyncDealUtil noticeAsyncDealUtil;

    @Autowired
    private FtbNoticeCategoriesService categoriesService;

    @Autowired
    private UserApiV2Util userApiV2Util;

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 查询公告列表
     *
     * @param req
     * @return
     */
    @Override
    public PageInfo<FtbNoticeAnnouncementsDto> getWebPageList(QueryAnnouncementListReq req) {
        if (StringUtils.isNotEmpty(req.getKeyWord())) {
            req.setKeyWord(req.getKeyWord().trim());
        }
        String loginUserId = UserProvider.getLoginUserId();
        //只有超级管理员才查所有，其他查询自己
        if (userApiV2Util.hasAllPermission()) {
            req.setInnerPowerUserId("");
        } else {
            req.setInnerPowerUserId(loginUserId);
        }
        Page<FtbNoticeAnnouncementsDto> queryPage = baseMapper.getWebPageList(Page.of(req.getCurrentPage(), req.getPageSize()), req);
        List<FtbNoticeAnnouncementsDto> records = queryPage.getRecords();
        //由于要显示一级和二级分类
        fillCategoryInfo(records);
        //发布人信息
        fillUserInfo(records, 1);
        //查询未提醒人员数量
        addNoAlertUserNum(records);
        addAcceptUserNum(records);

        //构建分页返回数据
        PageInfo<FtbNoticeAnnouncementsDto> pageInfo = new PageInfo<>();
        pageInfo.setList(records);
        pageInfo.setPageSize((int) queryPage.getSize());
        pageInfo.setPageNum((int) queryPage.getCurrent());
        pageInfo.setTotal((int) queryPage.getTotal());
        return pageInfo;
    }

    private void addAcceptUserNum(List<FtbNoticeAnnouncementsDto> records) {
        if (CollectionUtil.isEmpty(records)) {
            return;
        }
        List<String> adList = new ArrayList<>();
        for (FtbNoticeAnnouncementsDto record : records) {
            if (record.getStatus().equals(NoticeEnums.NoticeStatus.PUBLISHED.getCode())) {
                adList.add(record.getId());
                record.setUserCount(0L);
            }
        }
        if (CollectionUtil.isEmpty(adList)) {
            return;
        }
        List<BatchCommonCountDto> list = ftbNoticeAnnouncementsReceiveService.queryCountAcceptUserNum(adList);
        Map<String, Integer> ret = new HashMap<>();
        if (CollectionUtil.isNotEmpty(list)) {
            for (BatchCommonCountDto batchCommonCountDto : list) {
                ret.put(batchCommonCountDto.getSelectKey(), batchCommonCountDto.getNum());
            }
        }
        for (FtbNoticeAnnouncementsDto record : records) {
            if (record.getStatus().equals(NoticeEnums.NoticeStatus.PUBLISHED.getCode())) {
                Integer i = ret.get(record.getId());
                if (i != null) {
                    record.setUserCount(Long.valueOf(i));
                }
            }

        }

    }

    /**
     * 填充公告类别信息
     * 该方法根据提供的公告类别DTO记录列表，补充每个记录的类别名称列表信息
     * 它首先获取所有公告类别的信息，然后建立类别ID到类别的映射，
     * 以便快速查找每个记录所属类别的详细信息，并处理类别的层级关系
     *
     * @param records 公告类别DTO记录列表，泛型限定为AnnouncementsCategoryDto的子类
     */
    private <T extends AnnouncementsCategoryDto> void fillCategoryInfo(List<T> records) {

        if (CollectionUtil.isEmpty(records)) {
            return;
        }
        List<FtbNoticeCategories> allCate = categoriesService.listAll();
        if (CollectionUtil.isEmpty(allCate)) {
            return;
        }
        Map<String, FtbNoticeCategories> cateMap = new HashMap();
        for (FtbNoticeCategories ftbNoticeCategories : allCate) {
            cateMap.put(ftbNoticeCategories.getId(), ftbNoticeCategories);
        }
        for (AnnouncementsCategoryDto record : records) {
            FtbNoticeCategories ftbNoticeCategories = cateMap.get(record.getCategoryId());
            if (null == ftbNoticeCategories) {
                continue;
            }
            List<String> categoryNameList = new ArrayList<>();
            if (StringUtils.isEmpty(ftbNoticeCategories.getPath())) {
                categoryNameList.add(ftbNoticeCategories.getName());
            } else {
                List<String> allPreList = Arrays.asList(ftbNoticeCategories.getPath().split("-"));
                for (String s : allPreList) {
                    FtbNoticeCategories parent = cateMap.get(s);
                    if (null != parent) {
                        categoryNameList.add(parent.getName());
                    }
                }
                categoryNameList.add(ftbNoticeCategories.getName());
            }
            record.setCategoryNameList(categoryNameList);
        }
    }

    /**
     * 为公告记录添加未提醒用户数量
     * <p>
     * 此方法遍历给定的公告记录列表，对于每个已发布的公告，
     * 查询并设置未提醒用户数量该方法旨在更新公告对象，使其包含
     * 未被提醒的用户数量信息
     *
     * @param records 公告记录列表，不能为空
     */
    private void addNoAlertUserNum(List<FtbNoticeAnnouncementsDto> records) {
        if (CollectionUtil.isEmpty(records)) {
            return;
        }
        List<String> adList = new ArrayList<>();
        for (FtbNoticeAnnouncementsDto record : records) {
            if (record.getStatus().equals(NoticeEnums.NoticeStatus.PUBLISHED.getCode())) {
                adList.add(record.getId());
                record.setNoAlertUserCount(0L);
            }
        }
        if (CollectionUtil.isEmpty(adList)) {
            return;
        }
        List<BatchCommonCountDto> list = ftbNoticeAnnouncementsReceiveService.batchQueryNoAlertUserNum(adList);
        Map<String, Integer> ret = new HashMap<>();
        if (CollectionUtil.isNotEmpty(list)) {
            for (BatchCommonCountDto batchCommonCountDto : list) {
                ret.put(batchCommonCountDto.getSelectKey(), batchCommonCountDto.getNum());
            }
        }
        for (FtbNoticeAnnouncementsDto record : records) {
            if (record.getStatus().equals(NoticeEnums.NoticeStatus.PUBLISHED.getCode())) {
                Integer i = ret.get(record.getId());
                if (i != null) {
                    record.setNoAlertUserCount(Long.valueOf(i));
                }
            }

        }
    }

    /**
     * 查询用户信息
     *
     * @param records
     * @param type    1 创建人信息  2 发布人信息
     */

    private void fillUserInfo(List<FtbNoticeAnnouncementsDto> records, Integer type) {
        if (CollectionUtil.isEmpty(records)) {
            return;
        }
        List<String> userIds = new ArrayList<>();
        for (FtbNoticeAnnouncementsDto dto : records) {
            if (type == 1) {
                userIds.add(dto.getCreatorUserId());
            } else if (type == 2) {
                userIds.add(dto.getPublishUserId());
            }
        }

        Map<String, UserBoundVO> userPrimaryBoundBatch = userApiV2Util.getUserPrimaryBoundBatch(userIds, null);
        for (FtbNoticeAnnouncementsDto dto : records) {
            if (type == 1) {
                UserBoundVO userPrimaryBoundOne = userPrimaryBoundBatch.get(dto.getCreatorUserId());
                if (userPrimaryBoundOne != null) {
                    NoticeUserDto noticeUserDto = new NoticeUserDto();
                    noticeUserDto.setUserId(userPrimaryBoundOne.getId());
                    noticeUserDto.setUserName(userPrimaryBoundOne.getName());
                    noticeUserDto.setHeadLogo(UploaderUtil.uploaderImg(userPrimaryBoundOne.getHeadIcon()));
                    noticeUserDto.setFlowerName(userPrimaryBoundOne.getNickname());
                    UserOrgAndPositionDto userOrgAndPositionDto = NoticeUtils.getUserOrgAndPositionDto(userPrimaryBoundOne);
                    noticeUserDto.setOrgList(List.of(userOrgAndPositionDto));
                    dto.setCreateUserInfo(noticeUserDto);
                }else{
                    NoticeUserDto noticeUserDto = new NoticeUserDto();
                    noticeUserDto.setUserName(dto.getCreateUserName());
                    noticeUserDto.setUserId(dto.getCreatorUserId());
                    dto.setCreateUserInfo(noticeUserDto);
                }
            } else if (type == 2) {
                UserBoundVO userPrimaryBoundOne = userPrimaryBoundBatch.get(dto.getPublishUserId());
                if (userPrimaryBoundOne != null) {
                    NoticeUserDto noticeUserDto = new NoticeUserDto();
                    noticeUserDto.setUserId(userPrimaryBoundOne.getId());
                    noticeUserDto.setUserName(userPrimaryBoundOne.getName());
                    noticeUserDto.setHeadLogo(UploaderUtil.uploaderImg(userPrimaryBoundOne.getHeadIcon()));
                    noticeUserDto.setFlowerName(userPrimaryBoundOne.getNickname());
                    UserOrgAndPositionDto userOrgAndPositionDto = NoticeUtils.getUserOrgAndPositionDto(userPrimaryBoundOne);
                    noticeUserDto.setOrgList(List.of(userOrgAndPositionDto));
                    dto.setPublishUserInfo(noticeUserDto);
                }else{
                    NoticeUserDto noticeUserDto = new NoticeUserDto();
                    noticeUserDto.setUserName(dto.getPublicUserName());
                    noticeUserDto.setUserId(dto.getPublishUserId());
                    dto.setPublishUserInfo(noticeUserDto);
                }
            }
        }
    }

    /**
     * 获取应用公告分页列表
     *
     * @param req 查询应用公告列表的请求对象，包含查询条件和分页信息
     * @return 返回包含公告列表的分页信息的Dto（数据传输对象）对象
     */
    @Override
    public PageInfo<FtbNoticeAnnouncementsDto> getAppPageList(AppQueryAnnouncementListReq req) {
        if (StringUtils.isNotEmpty(req.getKeyWord())) {
            req.setKeyWord(req.getKeyWord().trim());
        }
        InnerAppQueryAnnouncementListReq innerReq = BeanUtil.copyProperties(req, InnerAppQueryAnnouncementListReq.class);
        if (StringUtils.isNotEmpty(req.getCategoryId())) {
            List<String> innerCate = getInnerCategory(innerReq.getCategoryId());
            innerReq.setInnerCategoryList(innerCate);
        }

        String loginUserId = UserProvider.getLoginUserId();
        //只有超级管理员才查所有，其他查询自己
        if (userApiV2Util.hasAllPermission()) {
            innerReq.setInnerPowerUserId("");
        } else {
            innerReq.setInnerPowerUserId(loginUserId);
        }

        Page<FtbNoticeAnnouncementsDto> queryPage = baseMapper.getAppPageList(Page.of(req.getCurrentPage(), req.getPageSize()), innerReq);
        List<FtbNoticeAnnouncementsDto> records = queryPage.getRecords();
        addAcceptUserNum(records);
        //构建分页返回数据
        PageInfo<FtbNoticeAnnouncementsDto> pageInfo = new PageInfo<>();
        pageInfo.setList(records);
        pageInfo.setPageSize((int) queryPage.getSize());
        pageInfo.setPageNum((int) queryPage.getCurrent());
        pageInfo.setTotal((int) queryPage.getTotal());
        return pageInfo;
    }

    /**
     * 新增公告
     *
     * @param req
     * @return
     */

    @Override
    @Transactional
    public FtbNoticeAnnouncements insertData(AddNoticeAnnouncementsReq req) {
        //检测参数
        Date now = new Date();
        checkAnnouncementsParam(req);
        NoticeEnums.SendSchedule sendSchedule = NoticeEnums.SendSchedule.fromCode(req.getIsScheduled());
        NoticeEnums.OptionButtonType opBtn = NoticeEnums.OptionButtonType.getByCode(req.getOptionBtn());

        categoriesService.queryAndCheckById(req.getCategoryId());
        //添加公告
        String content = noticeUtils.replaceAllScript(req.getContent());
        FtbNoticeAnnouncements entity = new FtbNoticeAnnouncements();
        entity.setCategoryId(req.getCategoryId());
        entity.setTitle(req.getTitle());
        entity.setShortContent(noticeUtils.clearHtmlTag(content, CONTENT_CUT_MAX_LENGTH));
        entity.setContent(content);
        entity.setNeedRead(req.getNeedRead());
        entity.setIsScheduled(req.getIsScheduled());
        entity.setCreateUserName(UserProvider.getUser().getUserName());
        if (sendSchedule == NoticeEnums.SendSchedule.SCHEDULED) {
            entity.setScheduledTime(req.getScheduledTime());
        }
        if (opBtn == NoticeEnums.OptionButtonType.DRAFT) {
            entity.setStatus(NoticeEnums.NoticeStatus.DRAFT.getCode());//草稿
        } else if (opBtn == NoticeEnums.OptionButtonType.PUBLISH) {
            if (sendSchedule == NoticeEnums.SendSchedule.IMMEDIATELY) {
                entity.setStatus(NoticeEnums.NoticeStatus.PUBLISHED.getCode());//已发布
                entity.setPublishUserId(UserProvider.getUser().getUserId());
                entity.setPublishTime(now);
            } else {
                entity.setStatus(NoticeEnums.NoticeStatus.PENDING.getCode()); //待发布
            }
        }


        entity.setReceiveUserType(req.getReceiveUserType());
        entity.setEnabledMark(NoticeEnums.DeleteType.VALID.getCode());
        NoticeEnums.NoticeStatus noticeStatus = NoticeEnums.NoticeStatus.fromCode(entity.getStatus());
        List<String> userList = getAnnouncementUserList(req, noticeStatus);
        entity.setUserCount(Long.valueOf(userList.size()));
        String moduleId = userApiV2Util.getPermissionModuleId();
        entity.setModuleId(moduleId);
        baseMapper.insert(entity);
        //处理附件
        if (CollectionUtil.isNotEmpty(req.getFiles())) {
            ftbNoticeFilesService.addFiles(entity.getId(), req.getFiles());
        }
        //添加用户
        if (CollectionUtil.isNotEmpty(userList)) {
            batchAddAnnouncementUser(entity.getId(), userList);
        }

        //发送推送消息
        if (sendSchedule == NoticeEnums.SendSchedule.IMMEDIATELY) {
            sendIM(userList, BeanUtil.copyProperties(entity, FtbNoticeAnnouncementsDto.class));
        }
        
        //如果是定时发送，将scheduledTime写入Redis，记录最大的发布时间
        if (sendSchedule == NoticeEnums.SendSchedule.SCHEDULED && req.getScheduledTime() != null) {
            updateMaxScheduledTime(entity.getId(),req.getScheduledTime());
        }
        
        //记录日志
        FtbNoticeAnnouncements copy = null;
        if (StringUtils.isNotEmpty(req.getCopyFrom())) {
            copy = baseMapper.selectById(req.getCopyFrom());
        }
        if (null != copy) {
            asyncRecordAnnouncementLogs(entity.getId(), entity.getTitle(), NoticeEnums.NoticeOperationLogType.COPY);
        } else {
            asyncRecordAnnouncementLogs(entity.getId(), entity.getTitle(), NoticeEnums.NoticeOperationLogType.ADD);
        }

        return entity;

    }

    /**
     * 修改公告
     *
     * @param id  公告id
     * @param req
     * @return
     */
    @Override
    @Transactional
    public FtbNoticeAnnouncements updateData(String id, UpdateNoticeAnnouncementsReq req) {
        //检测参数
        Date now = new Date();
        checkAnnouncementsParam(req);
        //检测公告信息
        FtbNoticeAnnouncements noticeAnnouncements = queryAndCheckById(id);
        NoticeEnums.NoticeStatus oldNoticeStaus = NoticeEnums.NoticeStatus.fromCode(noticeAnnouncements.getStatus());
        if (oldNoticeStaus == NoticeEnums.NoticeStatus.PUBLISHED) {
            throw new RuntimeException("公告已经发布不能修改");
        } else if (oldNoticeStaus == NoticeEnums.NoticeStatus.PENDING) {
            Date scheduledTime = noticeAnnouncements.getScheduledTime();
            // 判断发布日期不能超过当前日期
            if (now.after(scheduledTime)) {
                throw new RuntimeException("公告正在发布中，不能修改");
            }
        }
        categoriesService.queryAndCheckById(req.getCategoryId());

        NoticeEnums.SendSchedule sendSchedule = NoticeEnums.SendSchedule.fromCode(req.getIsScheduled());
        NoticeEnums.OptionButtonType opBtn = NoticeEnums.OptionButtonType.getByCode(req.getOptionBtn());

        //修改
        String content = noticeUtils.replaceAllScript(req.getContent());
        FtbNoticeAnnouncements entity = new FtbNoticeAnnouncements();
        entity.setId(id);
        entity.setCategoryId(req.getCategoryId());
        entity.setTitle(req.getTitle());
        entity.setShortContent(noticeUtils.clearHtmlTag(content, CONTENT_CUT_MAX_LENGTH));
        entity.setContent(content);
        entity.setNeedRead(req.getNeedRead());
        entity.setIsScheduled(req.getIsScheduled());
        if (sendSchedule == NoticeEnums.SendSchedule.SCHEDULED) {
            entity.setScheduledTime(req.getScheduledTime());
        }
        if (opBtn == NoticeEnums.OptionButtonType.DRAFT) {
            entity.setStatus(NoticeEnums.NoticeStatus.DRAFT.getCode());//草稿
        } else if (opBtn == NoticeEnums.OptionButtonType.PUBLISH) {
            if (sendSchedule == NoticeEnums.SendSchedule.IMMEDIATELY) {
                entity.setStatus(NoticeEnums.NoticeStatus.PUBLISHED.getCode());//已发布
                entity.setPublishUserId(UserProvider.getUser().getUserId());
                entity.setPublishTime(now);
            } else {
                entity.setStatus(NoticeEnums.NoticeStatus.PENDING.getCode()); //待发布
            }
        }

        entity.setReceiveUserType(req.getReceiveUserType());
        entity.setEnabledMark(NoticeEnums.DeleteType.VALID.getCode());
        NoticeEnums.NoticeStatus noticeStatus = NoticeEnums.NoticeStatus.fromCode(entity.getStatus());
        List<String> userList = getAnnouncementUserList(req, noticeStatus);
        entity.setUserCount(Long.valueOf(userList.size()));
        String moduleId = userApiV2Util.getPermissionModuleId();
        entity.setModuleId(moduleId);
        baseMapper.updateById(entity);
        //处理附件
        ftbNoticeFilesService.deleteFiles(id);
        if (CollectionUtil.isNotEmpty(req.getFiles())) {
            ftbNoticeFilesService.addFiles(entity.getId(), req.getFiles());
        }
        //处理老的接收用户
        ftbNoticeAnnouncementsReceiveService.dealAnnouncementUserWhenEdit(entity.getId(), userList, noticeStatus);

        //添加用户
        if (CollectionUtil.isNotEmpty(userList)) {
            batchAddAnnouncementUser(entity.getId(), userList);
        }
        //发送推送消息
        if (sendSchedule == NoticeEnums.SendSchedule.IMMEDIATELY) {
            sendIM(userList, BeanUtil.copyProperties(entity, FtbNoticeAnnouncementsDto.class));
        }
        
        //如果是定时发送，将scheduledTime写入Redis，记录最大的发布时间
        if (sendSchedule == NoticeEnums.SendSchedule.SCHEDULED && req.getScheduledTime() != null) {
            updateMaxScheduledTime(entity.getId(),req.getScheduledTime());
        }
        
        //记录日志
        asyncRecordAnnouncementLogs(entity.getId(), entity.getTitle(), NoticeEnums.NoticeOperationLogType.MODIFY);
        return entity;
    }

    /**
     * 确定公告用户列表
     *
     * @param req
     * @param noticeStatus
     * @return
     */
    private List<String> getAnnouncementUserList(AddNoticeAnnouncementsReq req, NoticeEnums.NoticeStatus noticeStatus) {
        NoticeEnums.ReceiveUserTypeEnum receiveUserTypeEnum = NoticeEnums.ReceiveUserTypeEnum.getByCode(req.getReceiveUserType());
        List<String> userList = new ArrayList<>();
        switch (noticeStatus) {
            case DRAFT://草稿
                if (receiveUserTypeEnum == NoticeEnums.ReceiveUserTypeEnum.SPECIFIC) {
                    //保存指定用户
                    userList = noticeUtils.uniqueStringList(req.getUserList());
                    userList = filterInvalidUser(userList);
                }
                break;
            case PENDING: //待发布
                if (receiveUserTypeEnum == NoticeEnums.ReceiveUserTypeEnum.SPECIFIC) {
                    //保存指定用户
                    userList = noticeUtils.uniqueStringList(req.getUserList());
                    userList = filterInvalidUser(userList);
                }
                break;
            case PUBLISHED://已发布
                if (receiveUserTypeEnum == NoticeEnums.ReceiveUserTypeEnum.SPECIFIC) {
                    //保存指定用户
                    userList = noticeUtils.uniqueStringList(req.getUserList());
                    userList = filterInvalidUser(userList);
                } else {
                    //保存全部用户
                    userList = noticeUtils.queryAllUserId();
                }
                break;
        }
        return userList;
    }

    //过滤已经删除和离职用户
    private List<String> filterInvalidUser(List<String> userList) {
        return noticeUtils.filterInvalidUser(userList);
    }

    /**
     * 公告参数校验
     *
     * @param req
     */
    private void checkAnnouncementsParam(AddNoticeAnnouncementsReq req) {
        Date now = new Date();
        NoticeEnums.NeedReadStatus needReadStatus = NoticeEnums.NeedReadStatus.fromCode(req.getNeedRead());
        if (null == needReadStatus) {
            throw new RuntimeException("必读状态参数错误");
        }
        NoticeEnums.SendSchedule sendSchedule = NoticeEnums.SendSchedule.fromCode(req.getIsScheduled());
        if (null == sendSchedule) {
            throw new RuntimeException("是否定时发送参数错误");
        }
        if (sendSchedule == NoticeEnums.SendSchedule.SCHEDULED) {
            if (null == req.getScheduledTime()) {
                throw new RuntimeException("定时发送日期不能为空");
            }
            // 判断过去日期是否超过当前日期
            if (req.getScheduledTime().before(now)) {
                throw new RuntimeException("定时发送日期必须大于当前日期");
            }
        }
        NoticeEnums.ReceiveUserTypeEnum receiveUserTypeEnum = NoticeEnums.ReceiveUserTypeEnum.getByCode(req.getReceiveUserType());
        if (null == receiveUserTypeEnum) {
            throw new RuntimeException("接收人员类型参数错误");
        }
        if (receiveUserTypeEnum == NoticeEnums.ReceiveUserTypeEnum.SPECIFIC) {
            req.setUserList(noticeUtils.uniqueStringList(req.getUserList()));
            if (CollectionUtil.isEmpty(req.getUserList())) {
                throw new RuntimeException("公告接收人员不能为空");
            }
        }
        NoticeEnums.OptionButtonType opBtn = NoticeEnums.OptionButtonType.getByCode(req.getOptionBtn());
        if (null == opBtn) {
            throw new RuntimeException("操作按钮类型参数错误");
        }
    }

    /**
     * 查询并检测公告是否存在
     *
     * @param id 公告id
     * @return
     */
    @Override
    public FtbNoticeAnnouncements queryAndCheckById(String id) {
        FtbNoticeAnnouncements entity = baseMapper.selectById(id);
        if (null == entity) {
            throw new RuntimeException("公告不存在");
        }
        if (entity.getEnabledMark().equals(NoticeEnums.DeleteType.INVALID.getCode())) {
            throw new RuntimeException("公告已经删除");
        }
        return entity;
    }

    /**
     * 查询公告基本信息
     *
     * @param id       公告id
     * @param terminal 终端【web or app】
     * @return
     */
    @Override
    public FtbNoticeAnnouncementsDetailDto getInfo(String id, String terminal) {
        FtbNoticeAnnouncements noticeAnnouncements = queryAndCheckById(id);
        FtbNoticeAnnouncementsDetailDto dto = BeanUtil.copyProperties(noticeAnnouncements, FtbNoticeAnnouncementsDetailDto.class);
        //查询分类名称
        if (StringUtils.isNotEmpty(noticeAnnouncements.getCategoryId())) {
            FtbNoticeCategories category = categoriesService.getById(noticeAnnouncements.getCategoryId());
            if (null != category) {
                dto.setCategoryName(category.getName());
                List<String> categoryIdList = new ArrayList<>();
                if (StringUtils.isEmpty(category.getPath())) {
                    categoryIdList.add(category.getId());
                } else {
                    categoryIdList.addAll(Arrays.asList(category.getPath().split("-")));
                    categoryIdList.add(category.getId());
                }
                dto.setCategoryIdList(categoryIdList);
            }
        }

        //查询用户接收人,只有指定才有
        if (noticeAnnouncements.getReceiveUserType().equals(NoticeEnums.ReceiveUserTypeEnum.SPECIFIC.getCode())) {
            List<FtbNoticeAnnouncementsReceiveDto> receiveDtoList = ftbNoticeAnnouncementsReceiveService.queryUserList(dto.getId());
            List<String> userIdList = new ArrayList<>();
            if (CollectionUtil.isNotEmpty(receiveDtoList)) {
                for (FtbNoticeAnnouncementsReceiveDto receiveDto : receiveDtoList) {
                    userIdList.add(receiveDto.getUserId());
                }
            }
            if(!noticeAnnouncements.getStatus().equals(3)){
                //过滤权限
                InnerPowerUserVO innerPowerUserVO = userApiV2Util.getLoginManagerUserIdsForEncode();
                if(innerPowerUserVO.getCode().equals(2)){
                    userIdList = new ArrayList<>();
                } else if (innerPowerUserVO.getCode().equals(1)) {
                    userIdList =UserApiV2Util.getIntersection(innerPowerUserVO.getUserIds(), userIdList);
                }
            }
            if ("web".equals(terminal)) {
                dto.setUserIdList(noticeUtils.queryEffectiveUserIds(userIdList, false));
            } else if ("app".equals(terminal)) {
                dto.setUserList(noticeUtils.FillUserInfo(userIdList, false));
            }
        }
        //查询附件信息
        dto.setFilesList(ftbNoticeFilesService.getFiles(dto.getId()));
        dto.setCreateUserInfo(noticeUtils.queryUserInfoForUserId(dto.getCreatorUserId()));
        if (dto.getStatus().equals(NoticeEnums.NoticeStatus.PUBLISHED.getCode())) {
            if (dto.getCreatorUserId().equals(dto.getPublishUserId())) {
                dto.setPublishUserInfo(dto.getCreateUserInfo());
            } else {
                dto.setPublishUserInfo(noticeUtils.queryUserInfoForUserId(dto.getPublishUserId()));
            }
            dto.setNoReadCount(ftbNoticeAnnouncementsReceiveService.queryReadStatusCount(noticeAnnouncements.getId(), NoticeEnums.ReadStatus.UNREAD.getCode()));
            dto.setReadCount(ftbNoticeAnnouncementsReceiveService.queryReadStatusCount(noticeAnnouncements.getId(), NoticeEnums.ReadStatus.READ.getCode()));
        } else {
            dto.setReadCount(0L);
            dto.setNoReadCount(0L);
        }
        return dto;
    }

    /**
     * 发布公告
     *
     * @param id 公告id
     */
    @Override
    @Transactional
    public void publish(String id) {
        Date now = new Date();
        //检测公告信息
        FtbNoticeAnnouncements noticeAnnouncements = queryAndCheckById(id);
        NoticeEnums.NoticeStatus oldNoticeStaus = NoticeEnums.NoticeStatus.fromCode(noticeAnnouncements.getStatus());
        if (oldNoticeStaus == NoticeEnums.NoticeStatus.PUBLISHED) {
            throw new RuntimeException("公告已经发布,请不要重复操作");
        }
        NoticeEnums.SendSchedule schedule = NoticeEnums.SendSchedule.fromCode(noticeAnnouncements.getIsScheduled());
        if (schedule == NoticeEnums.SendSchedule.SCHEDULED) {
            //修改发布状态
            FtbNoticeAnnouncements entity = new FtbNoticeAnnouncements();
            entity.setId(id);
            entity.setPublishUserId(UserProvider.getUser().getUserId());
            entity.setPublicUserName(UserProvider.getUser().getUserName());
            entity.setStatus(NoticeEnums.NoticeStatus.PENDING.getCode()); //待发布
            baseMapper.updateById(entity);
            return;
        }
        //获取用户ID列表
        NoticeEnums.ReceiveUserTypeEnum receiveUserTypeEnum = NoticeEnums.ReceiveUserTypeEnum.getByCode(noticeAnnouncements.getReceiveUserType());
        List<String> userList = getAnnouncementUserListForEntity(noticeAnnouncements, receiveUserTypeEnum);
        if (receiveUserTypeEnum == NoticeEnums.ReceiveUserTypeEnum.SPECIFIC) {
            userList = noticeUtils.filterInvalidUser(userList);
            //过滤权限
            InnerPowerUserVO innerPowerUserVO = userApiV2Util.getLoginManagerUserIdsForEncode();
            if(innerPowerUserVO.getCode().equals(2)){
                userList = new ArrayList<>();
            } else if (innerPowerUserVO.getCode().equals(1)) {
                userList =UserApiV2Util.getIntersection(innerPowerUserVO.getUserIds(), userList);
            }
        }

        //修改发布状态
        FtbNoticeAnnouncements entity = new FtbNoticeAnnouncements();
        entity.setId(id);
        entity.setPublishTime(now);
        entity.setPublishUserId(UserProvider.getUser().getUserId());
        entity.setStatus(NoticeEnums.NoticeStatus.PUBLISHED.getCode()); //待发布
        entity.setPublicUserName(UserProvider.getUser().getUserName());
        if (receiveUserTypeEnum == NoticeEnums.ReceiveUserTypeEnum.ALL) {
            entity.setUserCount(Long.valueOf(userList.size()));
        }
        baseMapper.updateById(entity);
        //如果是全部用户 就需要添加到用户接收表中
        if (receiveUserTypeEnum == NoticeEnums.ReceiveUserTypeEnum.ALL) {
            //添加到用户接收到表中
            ftbNoticeAnnouncementsReceiveService.cancelReceiveUser(noticeAnnouncements.getId());//先取消
            if (CollectionUtil.isNotEmpty(userList)) {
                batchAddAnnouncementUser(entity.getId(), userList);
            }
        }
        //发送推送消息
        sendIM(userList, BeanUtil.copyProperties(noticeAnnouncements, FtbNoticeAnnouncementsDto.class));

        //记录日志
        asyncRecordAnnouncementLogs(noticeAnnouncements.getId(), noticeAnnouncements.getTitle(), NoticeEnums.NoticeOperationLogType.PUBLISH);
    }

    /**
     * 取消发布
     *
     * @param id 公告id
     */
    @Override
    @Transactional
    public void cancelPublish(String id) {
        //检测公告信息
        FtbNoticeAnnouncements noticeAnnouncements = queryAndCheckById(id);
        NoticeEnums.NoticeStatus oldNoticeStaus = NoticeEnums.NoticeStatus.fromCode(noticeAnnouncements.getStatus());
        if (oldNoticeStaus != NoticeEnums.NoticeStatus.PUBLISHED) {
            throw new RuntimeException("公告未发布,不需要撤销");
        }

        //修改发布状态 撤销
        FtbNoticeAnnouncements entity = new FtbNoticeAnnouncements();
        entity.setId(id);
        entity.setStatus(NoticeEnums.NoticeStatus.WITHDRAWN.getCode()); //待发布
        baseMapper.updateById(entity);

        batchResetReadAndAlertAndWebCancel(entity.getId());
        //记录日志
        asyncRecordAnnouncementLogs(noticeAnnouncements.getId(), noticeAnnouncements.getTitle(), NoticeEnums.NoticeOperationLogType.REVOKE);
    }

    /**
     * 根据通知公告和接收用户类型获取接收用户列表
     *
     * @param noticeAnnouncements 通知公告对象，包含公告信息
     * @param receiveUserTypeEnum 用户接收类型枚举，确定公告发送给所有用户还是特定用户
     * @return 返回接收用户的ID列表
     */
    private List<String> getAnnouncementUserListForEntity(FtbNoticeAnnouncements noticeAnnouncements, NoticeEnums.ReceiveUserTypeEnum receiveUserTypeEnum) {

        List<String> userList = new ArrayList<>();
        switch (receiveUserTypeEnum) {
            case ALL:
                userList = noticeUtils.queryAllUserId();
                break;
            case SPECIFIC:
                List<FtbNoticeAnnouncementsReceiveDto> receiveDtoList = ftbNoticeAnnouncementsReceiveService.queryUserList(noticeAnnouncements.getId());
                if (CollectionUtil.isNotEmpty(receiveDtoList)) {
                    for (FtbNoticeAnnouncementsReceiveDto receiveDto : receiveDtoList) {
                        userList.add(receiveDto.getUserId());
                    }
                }
                //过滤权限
                InnerPowerUserVO innerPowerUserVO = userApiV2Util.getLoginManagerUserIdsForEncode();
                if(innerPowerUserVO.getCode().equals(2)){
                    userList = new ArrayList<>();
                } else if (innerPowerUserVO.getCode().equals(1)) {
                    userList =UserApiV2Util.getIntersection(innerPowerUserVO.getUserIds(), userList);
                }
                break;
        }
        return userList;
    }

    /**
     * 删除公告
     *
     * @param id 公告id
     */
    @Override
    @Transactional
    public void deleteData(String id) {
        FtbNoticeAnnouncements noticeAnnouncements = queryAndCheckById(id);
        NoticeEnums.NoticeStatus noticeStatus = NoticeEnums.NoticeStatus.fromCode(noticeAnnouncements.getStatus());
        if (noticeStatus == NoticeEnums.NoticeStatus.PUBLISHED) {
            throw new RuntimeException("公告已经发布不能删除");
        }
        //删除公告
        FtbNoticeAnnouncements entity = new FtbNoticeAnnouncements();
        entity.setId(id);
        entity.setEnabledMark(NoticeEnums.DeleteType.INVALID.getCode());
        baseMapper.updateById(entity);
        //删除公告接收人
        batchDeleteAnnouncementUser(entity.getId());
        //删除公告的附件
        batchDeleteAnnouncementFiles(entity.getId());
        //记录日志
        asyncRecordAnnouncementLogs(noticeAnnouncements.getId(), noticeAnnouncements.getTitle(), NoticeEnums.NoticeOperationLogType.DELETE);
    }

    /**
     * 提醒公告的一个用户
     *
     * @param announcementId 公告id
     * @param userId         用户id
     */
    @Override
    public void alertOne(String announcementId, String userId) {

        FtbNoticeAnnouncements noticeAnnouncements = queryAndCheckById(announcementId);
        NoticeEnums.NoticeStatus oldNoticeStaus = NoticeEnums.NoticeStatus.fromCode(noticeAnnouncements.getStatus());
        if (oldNoticeStaus != NoticeEnums.NoticeStatus.PUBLISHED) {
            throw new RuntimeException("对不起,公告未发布");
        }

        FtbNoticeAnnouncementsReceive ftbNoticeAnnouncementsReceive = ftbNoticeAnnouncementsReceiveService.queryUserForUserId(announcementId, userId);
        if (null == ftbNoticeAnnouncementsReceive) {
            return;
        }
        if (ftbNoticeAnnouncementsReceive.getReadStatus().equals(NoticeEnums.ReadStatus.READ.getCode())) {
            throw new RuntimeException("该用户已经阅读,无需提醒");
        }
        if (ftbNoticeAnnouncementsReceive.getAlertStatus().equals(NoticeEnums.AlertStatus.NOTIFIED.getCode())) {
            throw new RuntimeException("该用户已经提醒,无需重复操作");
        }
        //修改提醒状态
        ftbNoticeAnnouncementsReceiveService.updateCompleteAlert(announcementId, ftbNoticeAnnouncementsReceive.getUserId());
        //发送消息
        FtbNoticeAnnouncementsDto ftbNoticeAnnouncementsDto = BeanUtil.copyProperties(noticeAnnouncements, FtbNoticeAnnouncementsDto.class);
        ftbNoticeAnnouncementsDto.setInnerAlert(1);
        sendIM(List.of(ftbNoticeAnnouncementsReceive.getUserId()), ftbNoticeAnnouncementsDto);
        //记录日志
        asyncRecordAnnouncementLogs(noticeAnnouncements.getId(), noticeAnnouncements.getTitle(), NoticeEnums.NoticeOperationLogType.REMIND);
    }

    /**
     * 提醒公告相关的所有用户
     *
     * @param announcementId 公告id
     */
    @Override
    public void alertAll(String announcementId) {
        FtbNoticeAnnouncements noticeAnnouncements = queryAndCheckById(announcementId);
        NoticeEnums.NoticeStatus oldNoticeStaus = NoticeEnums.NoticeStatus.fromCode(noticeAnnouncements.getStatus());
        if (oldNoticeStaus != NoticeEnums.NoticeStatus.PUBLISHED) {
            throw new RuntimeException("对不起,公告未发布");
        }
        List<String> userList = new ArrayList<>();
        List<FtbNoticeAnnouncementsReceiveDto> receiveDtoList = ftbNoticeAnnouncementsReceiveService.queryNeedAlertUserList(announcementId);
        if (CollectionUtil.isNotEmpty(receiveDtoList)) {
            for (FtbNoticeAnnouncementsReceiveDto receiveDto : receiveDtoList) {
                userList.add(receiveDto.getUserId());
            }
        }
        ftbNoticeAnnouncementsReceiveService.updateCompleteAlertAll(announcementId);
        if (CollectionUtil.isNotEmpty(userList)) {
            FtbNoticeAnnouncementsDto ftbNoticeAnnouncementsDto = BeanUtil.copyProperties(noticeAnnouncements, FtbNoticeAnnouncementsDto.class);
            ftbNoticeAnnouncementsDto.setInnerAlert(1);
            sendIM(userList, ftbNoticeAnnouncementsDto);
        }

        //记录日志
        asyncRecordAnnouncementLogs(noticeAnnouncements.getId(), noticeAnnouncements.getTitle(), NoticeEnums.NoticeOperationLogType.REMIND);
    }

    /**
     * 定时检测定时发布的公告，到时间点就发布公告
     *
     * @param tenantId 租户id
     */
    @Override
    @Transactional
    public void checkPublishNotice(String tenantId) {
        // 先从 Redis 获取最大的定时发布时间
        String redisKey = String.format(NoticeUtils.NOTICE_SCHEDULED_MAX_TIME_KEY, tenantId);
        
        List<String> expiredIds = new ArrayList<>();
        Boolean hasKey = redisTemplate.hasKey(redisKey);
        if (Boolean.TRUE.equals(hasKey)) {
            // Redis key存在，查询缓存中所有数据
            Map<Object, Object> entries = redisTemplate.opsForHash().entries(redisKey);
            if (CollectionUtil.isEmpty(entries)) {
               return;
            }
            long currentTime = System.currentTimeMillis();
            // 遍历所有数据，判断当前时间是否大于缓存中的时间
            for (Map.Entry<Object, Object> entry : entries.entrySet()) {
                String id = (String) entry.getKey();
                Long scheduledTime = (Long) entry.getValue();
                // 如果当前时间大于缓存中的时间，说明该公告应该发布
                if (currentTime >= scheduledTime) {
                    expiredIds.add(id);
                }
            }
            if(CollectionUtil.isEmpty(expiredIds)){
                return;
            }
            // 删除缓存中对应的key
            if (CollectionUtil.isNotEmpty(expiredIds)) {
                for (String id : expiredIds) {
                    redisTemplate.opsForHash().delete(redisKey, id);
                }
            }
        }
        
        Date now = new Date();
        LambdaQueryWrapper<FtbNoticeAnnouncements> wrapper = new LambdaQueryWrapper<FtbNoticeAnnouncements>()
                .eq(FtbNoticeAnnouncements::getIsScheduled, NoticeEnums.SendSchedule.SCHEDULED.getCode())
                .eq(FtbNoticeAnnouncements::getStatus, NoticeEnums.NoticeStatus.PENDING.getCode())
                .lt(FtbNoticeAnnouncements::getScheduledTime, now)
                .eq(FtbNoticeAnnouncements::getEnabledMark, NoticeEnums.DeleteType.VALID.getCode());

        List<FtbNoticeAnnouncements> list = baseMapper.selectList(wrapper);
        if (CollectionUtil.isEmpty(list)) {
            return;
        }
        for (FtbNoticeAnnouncements announcements : list) {
            //查询用户
            List<String> userList = new ArrayList<>();
            NoticeEnums.ReceiveUserTypeEnum receiveUserTypeEnum = NoticeEnums.ReceiveUserTypeEnum.getByCode(announcements.getReceiveUserType());
            if (receiveUserTypeEnum == NoticeEnums.ReceiveUserTypeEnum.SPECIFIC) {//指定用户
                List<FtbNoticeAnnouncementsReceiveDto> ftbNoticeAnnouncementsReceiveDtos = ftbNoticeAnnouncementsReceiveService.queryUserList(announcements.getId());
                if (CollectionUtil.isNotEmpty(ftbNoticeAnnouncementsReceiveDtos)) {
                    for (FtbNoticeAnnouncementsReceiveDto ftbNoticeAnnouncementsReceiveDto : ftbNoticeAnnouncementsReceiveDtos) {
                        userList.add(ftbNoticeAnnouncementsReceiveDto.getUserId());
                    }
                }
                userList = noticeUtils.filterInvalidUser(userList);
                if (StringUtils.isNotEmpty(announcements.getModuleId())) {
                    //过滤权限
                   List<String> powerUserList = noticeUtils.queryPermissionForUserId(announcements.getCreatorUserId(), announcements.getModuleId(), tenantId);
                    if (CollectionUtil.isEmpty(powerUserList)) {
                        userList = new ArrayList<>();
                    } else {
                        userList = UserApiV2Util.getIntersection(powerUserList, userList);
                    }
                }
            } else {
                if (StringUtils.isNotEmpty(announcements.getModuleId())) {
                    try {
                        //查询全部用户
                        userList = noticeUtils.queryPermissionForUserId(announcements.getCreatorUserId(), announcements.getModuleId(), tenantId);
                        //添加到用户接收到表中
                        ftbNoticeAnnouncementsReceiveService.addReceiveUser(announcements.getId(), userList);
                    } catch (Exception e) {
                        log.error("查询用户权限范围内的用户失败", e);
                    }
                } else {
                    //兼容老数据没有模块id的情况
                    List<UserBoundVO> allUserList = userApiV2Util.getAllUserList(tenantId);
                    if (CollectionUtil.isNotEmpty(allUserList)) {
                        userList = allUserList.stream().map(UserBoundVO::getId).collect(Collectors.toList());
                        ftbNoticeAnnouncementsReceiveService.addReceiveUser(announcements.getId(), userList);
                    }
                }
            }
            //修改公告为已经发布
            FtbNoticeAnnouncements entity = new FtbNoticeAnnouncements();
            entity.setId(announcements.getId());
            entity.setPublishTime(now);
            String publishUserId = announcements.getLastModifyUserId();
            if (StringUtils.isEmpty(publishUserId)) {
                publishUserId = announcements.getCreatorUserId();
            }
            entity.setPublishUserId(publishUserId);
            entity.setStatus(NoticeEnums.NoticeStatus.PUBLISHED.getCode());
            entity.setUserCount(Long.valueOf(userList.size()));
            baseMapper.updateById(entity);
            //发送推送消息
            sendIM(userList, BeanUtil.copyProperties(announcements, FtbNoticeAnnouncementsDto.class), tenantId);
        }

    }

    /**
     * 完成阅读
     *
     * @param announcementId 公告ID
     * @param userId         用户ID
     */
    @Override
    public void completeRead(String announcementId, String userId) {
        ftbNoticeAnnouncementsReceiveService.updateCompleteRead(announcementId, userId);
    }

    /**
     * 分页查询当前用户的公告列表
     *
     * @param req
     * @return
     */
    @Override
    public PageInfo<MyNoticeAnnouncementsDto> getMyWebPageList(QueryMyWebAnnouncementListReq req) {
        if (StringUtils.isNotEmpty(req.getKeyWord())) {
            req.setKeyWord(req.getKeyWord().trim());
        }
        InnerQueryMyWebAnnouncementListReq innerReq = BeanUtil.copyProperties(req, InnerQueryMyWebAnnouncementListReq.class);
        String userId = UserProvider.getLoginUserId();
        innerReq.setUserId(userId);
        Page<MyNoticeAnnouncementsDto> queryPage = baseMapper.getMyWebPageList(Page.of(req.getCurrentPage(), req.getPageSize()), innerReq);
        List<MyNoticeAnnouncementsDto> records = queryPage.getRecords();
        fillCategoryInfo(records);
        //发布人信息
        fillUserInfoForMyNoticeAnnouncementsDto(records, 2);
        //构建分页返回数据
        PageInfo<MyNoticeAnnouncementsDto> pageInfo = new PageInfo<>();
        pageInfo.setList(records);
        pageInfo.setPageSize((int) queryPage.getSize());
        pageInfo.setPageNum((int) queryPage.getCurrent());
        pageInfo.setTotal((int) queryPage.getTotal());
        return pageInfo;
    }

    /**
     * 填充用户信息
     *
     * @param records
     * @param type    1:创建人 2:发布人
     */
    private void fillUserInfoForMyNoticeAnnouncementsDto(List<MyNoticeAnnouncementsDto> records, Integer type) {
        if (CollectionUtil.isEmpty(records)) {
            return;
        }
        List<String> userIds = new ArrayList<>();
        for (MyNoticeAnnouncementsDto dto : records) {
            if (type == 1) {
                userIds.add(dto.getCreatorUserId());
            } else if (type == 2) {
                userIds.add(dto.getPublishUserId());
            }
        }
        Map<String, UserBoundVO> userPrimaryBoundBatch = userApiV2Util.getUserPrimaryBoundBatch(userIds, null);
        for (MyNoticeAnnouncementsDto dto : records) {
            if (type == 1) {
                UserBoundVO userPrimaryBoundOne = userPrimaryBoundBatch.get(dto.getCreatorUserId());
                if (userPrimaryBoundOne != null) {
                    NoticeUserDto noticeUserDto = new NoticeUserDto();
                    noticeUserDto.setUserId(userPrimaryBoundOne.getId());
                    noticeUserDto.setUserName(userPrimaryBoundOne.getName());
                    noticeUserDto.setHeadLogo(UploaderUtil.uploaderImg(userPrimaryBoundOne.getHeadIcon()));
                    noticeUserDto.setFlowerName(userPrimaryBoundOne.getNickname());
                    UserOrgAndPositionDto userOrgAndPositionDto = NoticeUtils.getUserOrgAndPositionDto(userPrimaryBoundOne);
                    noticeUserDto.setOrgList(List.of(userOrgAndPositionDto));
                    dto.setCreateUserInfo(noticeUserDto);
                }else{
                    NoticeUserDto noticeUserDto = new NoticeUserDto();
                    noticeUserDto.setUserName(dto.getCreateUserName());
                    noticeUserDto.setUserId(dto.getCreatorUserId());
                    dto.setCreateUserInfo(noticeUserDto);
                }
            } else if (type == 2) {
                UserBoundVO userPrimaryBoundOne = userPrimaryBoundBatch.get(dto.getPublishUserId());
                if (userPrimaryBoundOne != null) {
                    NoticeUserDto noticeUserDto = new NoticeUserDto();
                    noticeUserDto.setUserId(userPrimaryBoundOne.getId());
                    noticeUserDto.setUserName(userPrimaryBoundOne.getName());
                    noticeUserDto.setHeadLogo(UploaderUtil.uploaderImg(userPrimaryBoundOne.getHeadIcon()));
                    noticeUserDto.setFlowerName(userPrimaryBoundOne.getNickname());
                    UserOrgAndPositionDto userOrgAndPositionDto = NoticeUtils.getUserOrgAndPositionDto(userPrimaryBoundOne);
                    noticeUserDto.setOrgList(List.of(userOrgAndPositionDto));
                    dto.setPublishUserInfo(noticeUserDto);
                }else{
                    NoticeUserDto noticeUserDto = new NoticeUserDto();
                    noticeUserDto.setUserName(dto.getPublicUserName());
                    noticeUserDto.setUserId(dto.getPublishUserId());
                    dto.setPublishUserInfo(noticeUserDto);
                }
            }
        }
    }

    /**
     * 获取当前用户公告列表的分页信息
     *
     * @param req 查询当前用户公告列表的请求参数，包括分页信息和查询条件等
     * @return 返回包含当前用户公告列表的分页信息对象，其中包含公告数据和分页元数据
     */
    @Override
    public PageInfo<MyNoticeAnnouncementsDto> getAppMyPageList(AppQueryMyAnnouncementListReq req) {
        if (StringUtils.isNotEmpty(req.getKeyWord())) {
            req.setKeyWord(req.getKeyWord().trim());
        }
        InnerAppQueryMyAnnouncementListReq innerReq = BeanUtil.copyProperties(req, InnerAppQueryMyAnnouncementListReq.class);
        String userId = UserProvider.getLoginUserId();
        innerReq.setUserId(userId);
        if (StringUtils.isNotEmpty(req.getCategoryId())) {
            List<String> innerCate = getInnerCategory(innerReq.getCategoryId());
            innerReq.setInnerCategoryList(innerCate);
        }
        Page<MyNoticeAnnouncementsDto> queryPage = baseMapper.getAppMyPageList(Page.of(req.getCurrentPage(), req.getPageSize()), innerReq);
        List<MyNoticeAnnouncementsDto> records = queryPage.getRecords();

        //构建分页返回数据
        PageInfo<MyNoticeAnnouncementsDto> pageInfo = new PageInfo<>();
        pageInfo.setList(records);
        pageInfo.setPageSize((int) queryPage.getSize());
        pageInfo.setPageNum((int) queryPage.getCurrent());
        pageInfo.setTotal((int) queryPage.getTotal());
        return pageInfo;
    }

    /**
     * 查询我的公告详细
     *
     * @param announcementId 公告ID
     * @param userId         用户ID
     * @return
     */
    @Override
    public MyNoticeAnnouncementsDetailDto getMyAnnouncementInfo(String announcementId, String userId) {
        FtbNoticeAnnouncements noticeAnnouncements = baseMapper.selectById(announcementId);
        if (null == noticeAnnouncements) {
            throw new RuntimeException("公告不存在");
        }

        //查询我的公告表
        FtbNoticeAnnouncementsReceive receive = ftbNoticeAnnouncementsReceiveService.queryUserForUserIdIngoreDelete(announcementId, userId);
        if (null == receive) {
            throw new RuntimeException("对不起,公告不存在");
        }
        MyNoticeAnnouncementsDetailDto dto = BeanUtil.copyProperties(noticeAnnouncements, MyNoticeAnnouncementsDetailDto.class);
        dto.setAlertStatus(receive.getAlertStatus());
        dto.setReadStatus(receive.getReadStatus());
        dto.setReadTime(receive.getReadTime());
        if (!noticeAnnouncements.getStatus().equals(NoticeEnums.NoticeStatus.PUBLISHED.getCode())) {
            dto.setIsCancel(1); // 0未撤销 1已撤销
        } else {
            dto.setIsCancel(receive.getIsCancel());
        }
        //查询分类名称
        if (StringUtils.isNotEmpty(noticeAnnouncements.getCategoryId())) {
            FtbNoticeCategories category = categoriesService.getById(noticeAnnouncements.getCategoryId());
            if (null != category) {
                dto.setCategoryName(category.getName());
            }
        }
        //查询附件信息
        dto.setFilesList(ftbNoticeFilesService.getFiles(dto.getId()));

        dto.setNoReadCount(ftbNoticeAnnouncementsReceiveService.queryReadStatusCount(announcementId, NoticeEnums.ReadStatus.UNREAD.getCode()));
        dto.setReadCount(ftbNoticeAnnouncementsReceiveService.queryReadStatusCount(announcementId, NoticeEnums.ReadStatus.READ.getCode()));
        dto.setCreateUserInfo(noticeUtils.queryUserInfoForUserId(dto.getCreatorUserId()));
        if (dto.getStatus().equals(NoticeEnums.NoticeStatus.PUBLISHED.getCode())) {
            if (dto.getCreatorUserId().equals(dto.getPublishUserId())) {
                dto.setPublishUserInfo(dto.getCreateUserInfo());
            } else {
                dto.setPublishUserInfo(noticeUtils.queryUserInfoForUserId(dto.getPublishUserId()));
            }
        }
        return dto;
    }

    /**
     * 查询用户未读消息
     *
     * @param userId   用户ID
     * @param num      数量
     * @param needRead 0:全部 1：非必读，2：必读
     * @return
     */
    @Override
    public List<MyNoticeAnnouncementsMsgDto> queryAllMyNoRead(String userId, Integer num, Integer needRead) {
        List<MyNoticeAnnouncementsMsgDto> list = baseMapper.queryAllMyNoRead(userId, num, needRead);
        if (CollectionUtil.isEmpty(list)) {
            return new ArrayList<>();
        }
        return list;
    }

    /**
     * web查询所有未读消息
     *
     * @param userId   用户ID
     * @param needRead 0:全部 1：非必读，2：必读
     * @return
     */
    @Override
    public List<MyNoticeAnnouncementsMsgDto> queryAllWebMyNoRead(String userId, Integer needRead) {
        List<MyNoticeAnnouncementsMsgDto> list = baseMapper.queryAllWebMyNoRead(userId, needRead);
        if (CollectionUtil.isEmpty(list)) {
            return new ArrayList<>();
        }
        return list;
    }


    /**
     * 查询用户最新的3条记录
     *
     * @param userId 用户ID
     * @param num    数量
     * @return
     */
    @Override
    public List<MyNoticeAnnouncementsDto> queryMyLatest(String userId, Integer num) {
        List<MyNoticeAnnouncementsDto> list = baseMapper.getAppMyList(userId, num);
        if (CollectionUtil.isEmpty(list)) {
            return new ArrayList<>();
        }
        return list;
    }

    /**
     * 获取指定类别的内部子类别列表
     * 该方法首先将当前类别ID添加到内部类别列表中，然后查询并添加其所有子类别的ID
     *
     * @param categoryId 类别ID
     * @return 包含该类别及其所有子类别ID的列表
     */
    private List<String> getInnerCategory(String categoryId) {
        List<String> innerCategoryList = new ArrayList<>();
        FtbNoticeCategories currCate = categoriesService.getById(categoryId);
        innerCategoryList.add(categoryId);
        List<String> categoryIds = categoriesService.queryChildCategoryIds(currCate);
        if (CollectionUtil.isNotEmpty(categoryIds)) {
            innerCategoryList.addAll(categoryIds);
        }
        return innerCategoryList;
    }

    /**
     * 发送即时消息
     * 此方法负责向指定的用户列表发送即时消息通知
     * 它会根据提供的类别ID设置通知的类别名称，如果类别ID有效，则异步发送即时消息
     *
     * @param userList 用户列表，用于接收即时消息通知
     * @param dto      通知公告数据传输对象，包含发送即时消息所需的信息
     * @param tenantId 租户ID，用于区分不同租户的消息发送环境
     */
    private void sendIM(List<String> userList, FtbNoticeAnnouncementsDto dto, String tenantId) {
        FtbNoticeCategories categories = categoriesService.getById(dto.getCategoryId());
        if (null != categories) {
            dto.setCategoryName(categories.getName());
        } else {
            dto.setCategoryName("");
        }
        noticeAsyncDealUtil.asyncSendIm(tenantId, userList, dto, noticeUtils.getHeadersForLogin());

    }

    /**
     * 异步IM通知提醒
     *
     * @param userList
     * @param dto
     */
    private void sendIM(List<String> userList, FtbNoticeAnnouncementsDto dto) {
        String tenantId = noticeUtils.getTenantId();
        sendIM(userList, dto, tenantId);
    }

    /**
     * 删除公告接受用户
     *
     * @param announcementId 公告id
     */

    private void batchDeleteAnnouncementUser(String announcementId) {
        String tenantId = noticeUtils.getTenantId();
        Map<String, String> headers = noticeUtils.getHeadersForLogin();
        noticeAsyncDealUtil.asyncDeleteAnnouncementMember(tenantId, announcementId, headers);
    }

    /**
     * 删除公告附件
     *
     * @param announcementId 公告id
     */
    private void batchDeleteAnnouncementFiles(String announcementId) {
        String tenantId = noticeUtils.getTenantId();
        Map<String, String> headers = noticeUtils.getHeadersForLogin();
        noticeAsyncDealUtil.asyncDeleteAnnouncementFiles(tenantId, announcementId, headers);
    }

    /**
     * 批量撤销接收人用户
     *
     * @param announcementId
     */
    private void batchResetReadAndAlertAndWebCancel(String announcementId) {
        String tenantId = noticeUtils.getTenantId();
        Map<String, String> headers = noticeUtils.getHeadersForLogin();
        noticeAsyncDealUtil.resetReadAndAlertAndWebCancel(tenantId, announcementId, headers);
    }

    /**
     * 批量添加接收人用户
     *
     * @param announcementId
     * @param userList
     */
    private void batchAddAnnouncementUser(String announcementId, List<String> userList) {
        String tenantId = noticeUtils.getTenantId();
        Map<String, String> headers = noticeUtils.getHeadersForLogin();
        noticeAsyncDealUtil.asyncAddAnnouncementMember(tenantId, announcementId, userList, headers);
    }


    /**
     * 异步记录通知公告操作日志
     *
     * @param announcementId
     */
    private void asyncRecordAnnouncementLogs(String announcementId, String title, NoticeEnums.NoticeOperationLogType opType) {
        UserInfo userInfo = UserProvider.getUser();
        String tenantId = noticeUtils.getTenantId();
        Map<String, String> headers = noticeUtils.getHeadersForLogin();
        FtbNoticeAnnouncementsLog log = new FtbNoticeAnnouncementsLog();
        log.setUserId(userInfo.getUserId());
        log.setUserName(userInfo.getUserName());
        log.setType(opType.getCode());
        log.setContent(opType.getDescription() + "公告:[" + title + "]");
        log.setBusinessId(announcementId);
        log.setEnabledMark(NoticeEnums.DeleteType.VALID.getCode());
        noticeAsyncDealUtil.asyncRecordAnnouncementLogs(tenantId, log, headers);
    }

    /**
     * 更新Redis中最大的定时发布时间
     *
     * @param scheduledTime 定时发布时间
     */
    private void updateMaxScheduledTime(String id,Date scheduledTime) {
    
        String redisKey = String.format(NoticeUtils.NOTICE_SCHEDULED_MAX_TIME_KEY, noticeUtils.getTenantId());
        // 将ID和时间写入Hash，key是id，value是时间戳
        redisTemplate.opsForHash().put(redisKey, id, scheduledTime.getTime());
            
    }

}




