# GmwrCommentsActionPlansServiceImpl.java

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

    import cloud.wconnect.commonlib.component.BeanMapper;
    import cloud.wconnect.commonlib.service.UserInternalService;
    import cloud.wconnect.commonlib.util.DateTimeUtils;
    import cloud.wconnect.workperformance.cons.gmwr.comment.GmwrCommentCategoryEnum;
    import cloud.wconnect.workperformance.cons.gmwr.comment.GmwrCommentItemEnum;
    import cloud.wconnect.workperformance.entity.gmwr.GmwrCommentsActionPlans;
    import cloud.wconnect.workperformance.mapper.gmwr.GmwrCommentsActionPlansMapper;
    import cloud.wconnect.workperformance.pojo.dto.gmwr.comment.GmwrCommentActionPlansDTO;
    import cloud.wconnect.workperformance.pojo.dto.gmwr.comment.GmwrCommentDTO;
    import cloud.wconnect.workperformance.pojo.dto.gmwr.comment.GmwrCommentSaveDTO;
    import cloud.wconnect.workperformance.pojo.dto.gmwr.note.GmwrNoteDTO;
    import cloud.wconnect.workperformance.service.gmwr.IGmwrCommentsActionPlansService;
    import com.baomidou.dynamic.datasource.annotation.DS;
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.google.common.collect.Lists;
    import jakarta.annotation.Resource;
    import org.apache.commons.collections.CollectionUtils;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.stereotype.Service;

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

    /**
     * <p>
     * GM Weekly Report Comments Action Plans 服务实现类
     * </p>
     *
     * @author zc
     * @since 2024-10-12
     */
    @Service
    @DS("work_performance")
    public class GmwrCommentsActionPlansServiceImpl extends ServiceImpl<GmwrCommentsActionPlansMapper, GmwrCommentsActionPlans> implements IGmwrCommentsActionPlansService {
        @Resource
        private UserInternalService userInternalService;
        @Resource
        private BeanMapper beanMapper;

        /**
         * 获取 Comment Action Plans
         *
         * @param tenantId   租户 id
         * @param propertyId 资产 id
         * @param reportId   周报 id
         * @return GmwrCommentActionPlansDTO
         * @author zc
         * @date 2024/10/19
         */
        @Override
        public List<GmwrCommentActionPlansDTO> list(int tenantId, int propertyId, int reportId) {
            List<GmwrCommentActionPlansDTO> actionPlansDTOList = generateActionPlansDTOList();
            List<GmwrCommentsActionPlans> actionPlanList = listByReportId(reportId);
            if (CollectionUtils.isNotEmpty(actionPlanList)) {
                Map<Integer, GmwrCommentsActionPlans> category2ActionPlans = actionPlanList.stream()
                        .collect(Collectors.toMap(GmwrCommentsActionPlans::getCategory, Function.identity()));
                Set<Integer> userIds = actionPlanList.stream().map(GmwrCommentsActionPlans::getUpdatedBy).collect(Collectors.toSet());
                Map<Integer, String> userId2Name = userInternalService.getNameMapByIds(tenantId, userIds);

                actionPlansDTOList.forEach(actionPlansDTO ->
                        actionPlansDTO.getList().forEach(commentDTO -> {
                            GmwrCommentsActionPlans actionPlans = category2ActionPlans.get(commentDTO.getCategory());
                            if (Objects.isNull(actionPlans)) {
                                return;
                            }
                            GmwrNoteDTO noteDTO = beanMapper.map(actionPlans, GmwrNoteDTO.class, noteDto ->
                                    noteDto.setUpdatedName(userId2Name.get(noteDto.getUpdatedBy())));
                            commentDTO.setNote(noteDTO);
                        })
                );
            }
            return actionPlansDTOList;
        }

        /**
         * 根据 reportId 获取 Action Plans
         *
         * @param reportId 周报 id
         * @return GmwrCommentsActionPlans 集合
         * @author zc
         * @date 2025/4/30
         */
        private List<GmwrCommentsActionPlans> listByReportId(int reportId) {
            return list(new LambdaQueryWrapper<GmwrCommentsActionPlans>()
                    .eq(GmwrCommentsActionPlans::getReportId, reportId));
        }

        /**
         * 生成 Action Plans DTO List
         *
         * @return List<GmwrCommentActionPlansDTO>
         * @author zc
         * @date 2025/3/3
         */
        private List<GmwrCommentActionPlansDTO> generateActionPlansDTOList() {
            return Arrays.stream(GmwrCommentCategoryEnum.values()).map(category -> {
                List<GmwrCommentDTO> list = category.getItems().stream().map(item -> {
                    GmwrCommentDTO itemDTO = new GmwrCommentDTO();
                    itemDTO.setCategory(item);
                    itemDTO.setCategoryName(GmwrCommentItemEnum.getCategoryName(item));
                    return itemDTO;
                }).toList();
                GmwrCommentActionPlansDTO actionPlansDTO = new GmwrCommentActionPlansDTO();
                actionPlansDTO.setCategory(category.getCategory());
                actionPlansDTO.setCategoryName(category.getCategoryName());
                actionPlansDTO.setList(list);
                return actionPlansDTO;
            }).toList();
        }

        /**
         * 保存 Comment Action Plans
         *
         * @param propertyId  资产 id
         * @param reportId    周报 id
         * @param saveDTOList comment DTO 集合
         * @param operatorId  操作人 id
         * @author zc
         * @date 2024/10/19
         */
        @Override
        public void save(int propertyId, int reportId, List<GmwrCommentSaveDTO> saveDTOList, int operatorId) {
            if (CollectionUtils.isEmpty(saveDTOList)) {
                return;
            }
            Map<Integer, GmwrCommentsActionPlans> category2Note = listByReportId(reportId).stream()
                    .collect(Collectors.toMap(GmwrCommentsActionPlans::getCategory, Function.identity()));

            long nowTimestamp = DateTimeUtils.nowTimestamp();
            List<GmwrCommentsActionPlans> commentsActionPlans = Lists.newArrayList();
            saveDTOList.forEach(item -> {
                GmwrCommentsActionPlans originComment = category2Note.get(item.getCategory());
                if (Objects.nonNull(originComment)) {
                    editNote(originComment, item.getContent(), operatorId, nowTimestamp);
                    commentsActionPlans.add(originComment);
                } else {
                    if (StringUtils.isBlank(item.getContent())) {
                        return;
                    }
                    commentsActionPlans.add(new GmwrCommentsActionPlans().setPropertyId(propertyId)
                            .setReportId(reportId).setCategory(item.getCategory())
                            .setContent(item.getContent()).setUpdatedBy(operatorId)
                            .setUpdatedTime(nowTimestamp));
                }
            });
            if (CollectionUtils.isEmpty(commentsActionPlans)) {
                return;
            }
            insertOrUpdateBatchByUnique(commentsActionPlans);
        }

        /**
         * 批量保存数据
         *
         * @param list 数据
         * @author zc
         * @date 2026/1/27
         */
        private void insertOrUpdateBatchByUnique(List<GmwrCommentsActionPlans> list) {
            if (CollectionUtils.isEmpty(list)) {
                return;
            }
            // 按唯一索引字段排序，避免死锁
            list.sort(Comparator.comparing(GmwrCommentsActionPlans::getReportId)
                    .thenComparing(GmwrCommentsActionPlans::getCategory));

            baseMapper.insertOrUpdateBatchByUnique(list);
        }

        /**
         * 编辑备注
         *
         * @param originNote   原始备注
         * @param content      内容
         * @param operatorId   操作人 id
         * @param nowTimestamp 当前时间戳
         * @author zc
         * @date 2025/4/30
         */
        private void editNote(GmwrCommentsActionPlans originNote, String content, int operatorId, long nowTimestamp) {
            if (Objects.equals(originNote.getContent(), content)) {
                return;
            }
            originNote.setContent(content);
            originNote.setUpdatedBy(operatorId);
            originNote.setUpdatedTime(nowTimestamp);
        }
    }