# OooRoomServiceImpl.java

    package cloud.wconnect.frontoffice.service.oooroom.impl;

    import cloud.wconnect.commonlib.component.BeanMapper;
    import cloud.wconnect.commonlib.cons.BaseConstant;
    import cloud.wconnect.commonlib.cons.BaseErrorCodeEnum;
    import cloud.wconnect.commonlib.cons.CommonErrorI18nConstant;
    import cloud.wconnect.commonlib.exception.BadRequestException;
    import cloud.wconnect.commonlib.pojo.dto.ListPagedDTO;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.redis.PropertyI18nRedisService;
    import cloud.wconnect.commonlib.util.DateTimeUtils;
    import cloud.wconnect.frontoffice.entity.oooroom.OooRoom;
    import cloud.wconnect.frontoffice.entity.oooroom.OooRoomTypeConfig;
    import cloud.wconnect.frontoffice.mapper.oooroom.OooRoomMapper;
    import cloud.wconnect.frontoffice.pojo.bo.clc.DataBO;
    import cloud.wconnect.frontoffice.pojo.bo.oooroom.OutOfOrderBO;
    import cloud.wconnect.frontoffice.pojo.dto.oooroom.OooRoomDTO;
    import cloud.wconnect.frontoffice.pojo.dto.oooroom.UpdateOooRoomDTO;
    import cloud.wconnect.frontoffice.pojo.query.oooroom.OooRoomQuery;
    import cloud.wconnect.frontoffice.service.oooroom.IOooRoomService;
    import cloud.wconnect.frontoffice.service.oooroom.IOooRoomTypeConfigService;
    import com.baomidou.dynamic.datasource.annotation.DS;
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.google.common.collect.Lists;
    import com.google.common.collect.Sets;
    import jakarta.annotation.Resource;
    import org.apache.commons.collections.CollectionUtils;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Lazy;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;

    import java.time.LocalDate;
    import java.time.ZoneId;
    import java.time.ZonedDateTime;
    import java.time.format.DateTimeFormatter;
    import java.util.*;
    import java.util.stream.Collectors;

    /**
     * <p>
     * 服务实现类
     * </p>
     *
     * @author yyt
     * @since 2023-08-05
     */
    @DS("ratelog_manage")
    @Service
    public class OooRoomServiceImpl extends ServiceImpl<OooRoomMapper, OooRoom> implements IOooRoomService {

        @Autowired
        private BeanMapper beanMapper;
        @Lazy
        @Resource
        private IOooRoomTypeConfigService oooRoomTypeConfigService;
        @Autowired
        private PropertyI18nRedisService propertyI18nRedisService;

        /**
         * 校验存在
         *
         * @param id ooo 数据 id
         * @return ooo 数据
         * @author yyt
         * @date 2023/8/5
         */
        @Override
        public OooRoom checkExist(long id) {
            OooRoom oooRoom = getById(id);
            if (Objects.isNull(oooRoom)) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_NOT_FOUND,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "the oooRoom is not found;id:" + id);
            }
            return oooRoom;
        }

        /**
         * 处理保存 ooo
         *
         * @param propertyDTO      资产
         * @param updateOooRoomDTO ooo 数据
         * @param userId           操作人
         * @author yyt
         * @date 2023/8/5
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void handleSave(PropertyDTO propertyDTO, UpdateOooRoomDTO updateOooRoomDTO, int userId) {
            // 处理有交集的数据
            handleHaveIntersectionData(propertyDTO, updateOooRoomDTO.getRoomType(), updateOooRoomDTO.getFromDate(), updateOooRoomDTO.getToDate(),
                    updateOooRoomDTO.getRoomNumbers(), null);

            Boolean closedNow = updateOooRoomDTO.getClosedNow();
            long dateNow = DateTimeUtils.dateNowTimestamp(propertyDTO.getTimezone());
            long now = DateTimeUtils.nowTimestamp();
            OooRoom newOooRoom = new OooRoom().setPropertyId(propertyDTO.getId()).setRoomNumbers(updateOooRoomDTO.getRoomNumbers())
                    .setRoomType(updateOooRoomDTO.getRoomType()).setFromDate(updateOooRoomDTO.getFromDate())
                    .setToDate(updateOooRoomDTO.getToDate()).setCompletedDate(closedNow ? dateNow : 0L)
                    .setRemarks(updateOooRoomDTO.getRemarks()).setUniqKeyForSync("").setCreatedBy(userId)
                    .setCreatedTime(now).setCompletedBy(closedNow ? userId : 0).setCompletedTime(closedNow ? now : 0L)
                    .setDataDate(0L);
            newOooRoom.setUniqKey();
            save(newOooRoom);
        }

        /**
         * 根据选择条件检索对应的 ooo 数据
         *
         * @param propertyId  资产
         * @param roomTypes   类型
         * @param roomNumbers 房间号
         * @param id          忽略数据id
         * @return ooo 数据
         * @author yyt
         * @date 2023/8/5
         */
        @Override
        public List<OooRoom> listByCondition(Integer propertyId, List<Integer> roomTypes, List<String> roomNumbers, Long id) {
            return list(new QueryWrapper<OooRoom>().lambda().eq(OooRoom::getPropertyId, propertyId)
                    .in(OooRoom::getRoomType, roomTypes).ne(Objects.nonNull(id), OooRoom::getId, id)
                    .apply("concat(',',room_numbers,',') regexp concat(',(', {0}, '),')", String.join("|", roomNumbers)));
        }

        /**
         * 处理交集数据
         *
         * @param propertyDTO 资产
         * @param roomType    类型
         * @param fromDate    开始日期
         * @param toDate      结束日期
         * @param roomNumbers 房间号
         * @param id          排除自身
         * @author yyt
         * @date 2023/8/7
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void handleHaveIntersectionData(PropertyDTO propertyDTO, Integer roomType, Long fromDate, Long toDate, String roomNumbers, Long id) {
            // 查询已存在数据中 跟当前房间号的起止时间有交集的 数据，删除掉这些数据，保证同一时间段内 一个房间号 不能出现多条数据
            List<OooRoom> updateOooRooms = Lists.newArrayList();
            List<String> roomNumberList = Arrays.stream(roomNumbers.split(",")).collect(Collectors.toList());
            List<OooRoom> oooRooms = listByCondition(propertyDTO.getId(), Lists.newArrayList(roomType), roomNumberList, id);

            if (CollectionUtils.isEmpty(oooRooms)) {
                return;
            }
            List<OooRoom> haveIntersectionOooRoomByRoomNumbers = oooRooms.stream().filter(item -> {
                // 过滤出有交集的数据
                return !(item.getFromDate() > toDate || item.getToDate() < fromDate);
            }).toList();
            if (haveIntersectionOooRoomByRoomNumbers.isEmpty()) {
                return;
            }

            long dateNow = DateTimeUtils.dateNowTimestamp(propertyDTO.getTimezone());
            long now = DateTimeUtils.nowTimestamp();

            haveIntersectionOooRoomByRoomNumbers.forEach(item -> {
                // 过滤掉有交集的房间号
                String noIntersectionRoomNumbers = Arrays.stream(item.getRoomNumbers().split(","))
                        .filter(roomNumber -> !roomNumbers.contains(roomNumber)).collect(Collectors.joining(","));
                if (StringUtils.isEmpty(noIntersectionRoomNumbers)) {
                    // 标记为已完成
                    if (item.getCompletedDate() == BaseConstant.LONG_ZERO) {
                        OooRoom updateOooRoom = beanMapper.map(item, OooRoom.class);
                        updateOooRoom.setCompletedDate(dateNow);
                        updateOooRoom.setCompletedTime(now);
                        updateOooRoom.setUniqKey();
                        updateOooRooms.add(updateOooRoom);
                    }
                } else {
                    // 修改数据
                    OooRoom updateOooRoom = beanMapper.map(item, OooRoom.class);
                    updateOooRoom.setRoomNumbers(noIntersectionRoomNumbers);
                    updateOooRoom.setUniqKey();
                    updateOooRooms.add(updateOooRoom);
                }
            });
            if (!updateOooRooms.isEmpty()) {
                updateBatchById(updateOooRooms);
            }
        }

        /**
         * 处理修改
         *
         * @param updateBeforeOooRoom 修改之前的数据
         * @param updateOooRoomDTO    修改内容
         * @param userId              操作人
         * @author yyt
         * @date 2023/8/7
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void handleUpdate(OooRoom updateBeforeOooRoom, UpdateOooRoomDTO updateOooRoomDTO, int userId) {
            PropertyDTO propertyDTO = propertyI18nRedisService.get(updateBeforeOooRoom.getPropertyId());
            handleHaveIntersectionData(propertyDTO, updateOooRoomDTO.getRoomType(), updateOooRoomDTO.getFromDate(), updateOooRoomDTO.getToDate(),
                    updateOooRoomDTO.getRoomNumbers(), updateBeforeOooRoom.getId());

            OooRoom updateContent = new OooRoom();
            updateContent.setId(updateBeforeOooRoom.getId());
            updateContent.setPropertyId(updateBeforeOooRoom.getPropertyId());
            updateContent.setRoomNumbers(updateOooRoomDTO.getRoomNumbers());
            updateContent.setRoomType(updateOooRoomDTO.getRoomType());
            updateContent.setFromDate(updateOooRoomDTO.getFromDate());
            updateContent.setToDate(updateOooRoomDTO.getToDate());
            updateContent.setRemarks(updateOooRoomDTO.getRemarks());

            if (updateOooRoomDTO.getClosedNow()) {
                updateContent.setCompletedBy(userId);
                String timeZone = propertyDTO.getTimezone();
                updateContent.setCompletedDate(DateTimeUtils.dateNowTimestamp(timeZone));
                updateContent.setCompletedTime(DateTimeUtils.nowTimestamp());
            }

            // 重置唯一 key
            updateContent.setUniqKey();

            updateContent.setUpdatedBy(userId);
            updateContent.setUpdatedTime(DateTimeUtils.nowTimestamp());
            updateById(updateContent);
        }

        /**
         * 获取所有未完成的房间
         *
         * @param propertyId 资产 id
         * @return 未完成的房间
         * @author yyt
         * @date 2023/8/8
         */
        @Override
        public List<OooRoom> listAllNotComplete(Integer propertyId) {
            return list(new QueryWrapper<OooRoom>().lambda().eq(OooRoom::getPropertyId, propertyId)
                    .eq(OooRoom::getCompletedDate, BaseConstant.ZERO));
        }

        /**
         * 处理 ooo room 数据
         *
         * @param propertyDTO 资产
         * @param operator    操作人
         * @param dataBO      ooo room 数据
         * @author yyt
         * @date 2023/8/9
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void handleOooRoomData(PropertyDTO propertyDTO, int operator, DataBO<OutOfOrderBO> dataBO) {
            // 合并数据
            List<OooRoom> oooRooms = mergeSameDataRangeData(propertyDTO, dataBO.getData());

            // 做重复导入处理
            handleRepeatImport(propertyDTO, dataBO.getDate());

            if (!oooRooms.isEmpty()) {
                // 查询已经存在的 ooo 时间区间在选择日期内的数据
                String timeZone = propertyDTO.getTimezone();
                DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(BaseConstant.DATE_FORMAT_YYYY_MM_DD);
                ZonedDateTime targetDate = LocalDate.parse(dataBO.getDate(), dateTimeFormatter)
                        .atStartOfDay(ZoneId.of(timeZone));
                long targetDateSecond = targetDate.toEpochSecond();
                Integer propertyId = propertyDTO.getId();
                List<OooRoom> existOooRooms = listForTargetDateInDateRange(propertyId, targetDateSecond);

                long nowTime = DateTimeUtils.nowTimestamp();
                Set<String> newUniqKeys = Sets.newHashSetWithExpectedSize(oooRooms.size());
                // 标识是否存在前一天的数据记录
                boolean includeYesterday = false;
                for (OooRoom oooRoom : oooRooms) {
                    oooRoom.setCreatedBy(operator);
                    oooRoom.setCreatedTime(nowTime);
                    oooRoom.setDataDate(targetDateSecond);
                    // 生成唯一标识
                    oooRoom.setUniqKey();
                    newUniqKeys.add(oooRoom.getUniqKeyForSync());

                    if (!includeYesterday && (oooRoom.getToDate() < targetDateSecond)) {
                        includeYesterday = true;
                    }
                }
                if (includeYesterday) {
                    // 再查询已同步过的前一日的数据（报告拿的是前一天 + 当天）
                    existOooRooms.addAll(listForTargetDateInDateRange(propertyId, targetDate.minusDays(1).toEpochSecond()));
                }

                // 已存在数据的唯一标识
                List<String> existUniqKeys = existOooRooms.stream().map(OooRoom::getUniqKeyForSync).collect(Collectors.toList());

                // 获取已存在数据中没有 而将要同步的数据中有的唯一标识，进行新增
                List<OooRoom> newOooRooms = oooRooms.stream().filter(oooRoom -> !existUniqKeys.contains(oooRoom.getUniqKeyForSync()))
                        .collect(Collectors.toList());
                if (!newOooRooms.isEmpty()) {
                    // 新增前，检查数据库中是否存在和 新增数据中的房间号有交集的，如果有，则标记为已完成
                    List<OooRoom> updateOooRooms = Lists.newArrayList();
                    List<Long> deleteOooRoomIds = Lists.newArrayList();
                    List<String> roomNumberList = newOooRooms.stream().flatMap(item -> Arrays.stream(item.getRoomNumbers()
                            .split(","))).distinct().collect(Collectors.toList());
                    List<Integer> roomTypes = newOooRooms.stream().map(OooRoom::getRoomType).distinct().collect(Collectors.toList());
                    List<OooRoom> oooRoomsForRoomNumbers = listByCondition(propertyId, roomTypes, roomNumberList, null);
                    Map<Integer, List<OooRoom>> oooRoomsByRoomType = oooRoomsForRoomNumbers.stream().collect(Collectors.groupingBy(OooRoom::getRoomType));
                    newOooRooms.forEach(item -> {
                        List<String> roomNumbers = Arrays.stream(item.getRoomNumbers().split(",")).toList();
                        // 过滤出跟当前 ooo room 起止时间有交集的数据
                        List<OooRoom> oooRoomsForRoomType = oooRoomsByRoomType.getOrDefault(item.getRoomType(), Collections.emptyList());
                        List<OooRoom> haveIntersectionOooRoom = oooRoomsForRoomType.stream()
                                .filter(oooRoom -> Arrays.stream(oooRoom.getRoomNumbers().split(","))
                                        .anyMatch(roomNumbers::contains)).collect(Collectors.toList());

                        if (CollectionUtils.isEmpty(haveIntersectionOooRoom)) {
                            return;
                        }
                        Long fromDate = item.getFromDate();
                        Long toDate = item.getToDate();
                        List<OooRoom> haveIntersectionOooRoomByRoomNumbers = haveIntersectionOooRoom.stream().filter(oooRoom -> {
                            // 过滤出有交集的数据
                            return !(oooRoom.getFromDate() > toDate || oooRoom.getToDate() < fromDate);
                        }).toList();
                        if (haveIntersectionOooRoomByRoomNumbers.isEmpty()) {
                            return;
                        }

                        long dateNow = DateTimeUtils.dateNowTimestamp(timeZone);
                        long now = DateTimeUtils.nowTimestamp();
                        // 有交集的标记为已完成
                        haveIntersectionOooRoomByRoomNumbers.forEach(oooRoom -> {
                            if (oooRoom.getRoomNumbers().equals(item.getRoomNumbers())) {
                                // 当已存在的故障房为 未完成状态时，如果报告中故障房的日期区间跟已存在数据一致，则删除掉数据库中已存在的数据
                                if (oooRoom.getCompletedDate() == 0) {
                                    if (oooRoom.getFromDate().equals(fromDate) && oooRoom.getToDate().equals(toDate)) {
                                        deleteOooRoomIds.add(oooRoom.getId());
                                        return;
                                    }
                                }
                            }
                            if (oooRoom.getCompletedDate() == BaseConstant.LONG_ZERO) {
                                OooRoom updateOooRoom = beanMapper.map(oooRoom, OooRoom.class);
                                updateOooRoom.setCompletedDate(dateNow);
                                updateOooRoom.setCompletedTime(now);
                                updateOooRoom.setUniqKey();
                                updateOooRooms.add(updateOooRoom);
                            }
                        });
                    });
                    if (!updateOooRooms.isEmpty()) {
                        updateBatchById(updateOooRooms);
                    }
                    if (!deleteOooRoomIds.isEmpty()) {
                        removeByIds(deleteOooRoomIds);
                    }
                    saveBatch(newOooRooms);
                }

                // 处理历史数据（每天导入报告都是全量保存数据，同一条数据多次导入报告后，如果后续导入报告时清除了当前数据，则标记历史数据为已完成）
                existUniqKeys.removeAll(newUniqKeys);
                if (!existUniqKeys.isEmpty()) {
                    OooRoom updateContent = new OooRoom();
                    updateContent.setCompletedDate(DateTimeUtils.dateNowTimestamp(timeZone));
                    updateContent.setCompletedTime(DateTimeUtils.nowTimestamp());
                    update(updateContent, new QueryWrapper<OooRoom>().lambda().eq(OooRoom::getPropertyId, propertyId)
                            .in(OooRoom::getUniqKeyForSync, existUniqKeys));
                }
            }
        }

        /**
         * 合并相同日期区间的数据
         *
         * @param propertyDTO      资产
         * @param outOfOrderBOList out of order
         * @return 合并后的数据
         * @author yyt
         * @date 2023/8/9
         */
        private List<OooRoom> mergeSameDataRangeData(PropertyDTO propertyDTO, List<OutOfOrderBO> outOfOrderBOList) {
            if (CollectionUtils.isEmpty(outOfOrderBOList)) {
                return Collections.emptyList();
            }

            Map<String, List<OutOfOrderBO>> oooRoomListByUnionKey = outOfOrderBOList.stream()
                    .collect(Collectors.groupingBy(oooRoomBO -> StringUtils.join(oooRoomBO.getRoomTypeSymbol(),
                            oooRoomBO.getFromDate(), oooRoomBO.getToDate(), oooRoomBO.getCompletedDate(), oooRoomBO.getRemarks())));

            // 查询类型配置
            Integer propertyId = propertyDTO.getId();
            Map<String, Integer> roomTypeIdByRoomTypeSymbol = oooRoomTypeConfigService.list(new QueryWrapper<OooRoomTypeConfig>()
                            .lambda().eq(OooRoomTypeConfig::getPropertyId, propertyDTO.getId()))
                    .stream().collect(Collectors.toMap(item -> item.getRoomTypeSymbol().toLowerCase(), OooRoomTypeConfig::getId));

            ZoneId zoneId = ZoneId.of(propertyDTO.getTimezone());
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(BaseConstant.DATE_FORMAT_YYYY_MM_DD);
            List<OooRoom> result = Lists.newArrayListWithExpectedSize(oooRoomListByUnionKey.size());
            oooRoomListByUnionKey.forEach((key, oooRoomBOList) -> {
                OooRoom oooRoom = new OooRoom();
                oooRoom.setRoomNumbers(oooRoomBOList.stream().sorted(Comparator.comparing(OutOfOrderBO::getRoomNumber))
                        .map(OutOfOrderBO::getRoomNumber).collect(Collectors.joining(",")));
                OutOfOrderBO outOfOrderBO = oooRoomBOList.get(0);
                oooRoom.setPropertyId(propertyId);
                oooRoom.setFromDate(LocalDate.parse(outOfOrderBO.getFromDate(), dateTimeFormatter).atStartOfDay(zoneId).toEpochSecond());
                oooRoom.setToDate(LocalDate.parse(outOfOrderBO.getToDate(), dateTimeFormatter).atStartOfDay(zoneId).toEpochSecond());
                oooRoom.setCompletedDate(StringUtils.isEmpty(outOfOrderBO.getCompletedDate()) ? BaseConstant.LONG_ZERO :
                        LocalDate.parse(outOfOrderBO.getCompletedDate(), dateTimeFormatter).atStartOfDay(zoneId).toEpochSecond());
                oooRoom.setRemarks(outOfOrderBO.getRemarks());
                oooRoom.setRoomType(roomTypeIdByRoomTypeSymbol.get(outOfOrderBO.getRoomTypeSymbol().toLowerCase()));
                result.add(oooRoom);
            });
            return result;
        }

        /**
         * 处理重复导入
         *
         * @param propertyDTO 资产
         * @param date        指定日期
         * @author yyt
         * @date 2023/8/9
         */
        private void handleRepeatImport(PropertyDTO propertyDTO, String date) {
            // 清除选择日期内数据
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(BaseConstant.DATE_FORMAT_YYYY_MM_DD);
            remove(new QueryWrapper<OooRoom>().lambda().eq(OooRoom::getPropertyId, propertyDTO.getId())
                    .eq(OooRoom::getDataDate, LocalDate.parse(date, dateTimeFormatter)
                            .atStartOfDay(ZoneId.of(propertyDTO.getTimezone())).toEpochSecond()));
        }

        /**
         * 查询 ooo 日期区间包含指定日期的 ooo 数据
         *
         * @param propertyId 资产 id
         * @param targetDate 指定日期
         * @return ooo 数据
         * @author yyt
         * @date 2023/8/9
         */
        @Override
        public List<OooRoom> listForTargetDateInDateRange(Integer propertyId, Long targetDate) {
            return list(new QueryWrapper<OooRoom>().lambda().eq(OooRoom::getPropertyId, propertyId)
                    .le(OooRoom::getFromDate, targetDate).ge(OooRoom::getToDate, targetDate));
        }

        /**
         * 分页查询数据
         *
         * @param propertyId 资产 id
         * @param query      查询条件
         * @return ooo room
         * @author yyt
         * @date 2024/11/20
         **/
        @Override
        public Page<OooRoom> listPage(Integer propertyId, OooRoomQuery query) {
            return page(new Page<>(query.getPage(), query.getSize()), packageSql(propertyId, query)
                    .orderByAsc(OooRoom::getRoomType, OooRoom::getFromDate));
        }

        /**
         * 查询数据
         *
         * @param propertyId 资产 id
         * @param query      查询条件
         * @return ooo room
         * @author yyt
         * @date 2024/11/20
         **/
        @Override
        public List<OooRoom> list(Integer propertyId, OooRoomQuery query) {
            return list(packageSql(propertyId, query));
        }

        /**
         * 封装查询 sql
         *
         * @param propertyId 资产 id
         * @param query      查询条件
         * @return sql
         * @author yyt
         * @date 2024/11/20
         **/
        private LambdaQueryWrapper<OooRoom> packageSql(Integer propertyId, OooRoomQuery query) {
            LambdaQueryWrapper<OooRoom> sqlCondition = new QueryWrapper<OooRoom>()
                    .lambda().eq(OooRoom::getPropertyId, propertyId)
                    .like(StringUtils.isNotEmpty(query.getRoomNumber()), OooRoom::getRoomNumbers, query.getRoomNumber())
                    .eq(Objects.nonNull(query.getRoomType()), OooRoom::getRoomType, query.getRoomType());
            if (Objects.nonNull(query.getStatus())) {
                if (query.getStatus() == OooRoomQuery.STATUS_OPENING) {
                    sqlCondition.eq(OooRoom::getCompletedDate, BaseConstant.ZERO);
                } else {
                    sqlCondition.gt(OooRoom::getCompletedDate, BaseConstant.ZERO);
                }
            }
            if (Objects.nonNull(query.getFromDate())) {
                sqlCondition.le(OooRoom::getFromDate, query.getFromDate()).ge(OooRoom::getToDate, query.getFromDate());
            }
            return sqlCondition;
        }

        /**
         * 删除 ooo room
         *
         * @param id 数据 id
         * @author yyt
         * @date 2024/11/20
         **/
        @Override
        public void delete(long id) {
            removeById(id);
        }

        /**
         * 获取 ooo room
         *
         * @param propertyId 资产 id
         * @param query      查询条件
         * @return ooo room
         * @author yyt
         * @date 2026/1/19
         **/
        @Override
        public ListPagedDTO<OooRoomDTO> listOooRoomDTO(Integer propertyId, OooRoomQuery query) {
            long total;
            List<OooRoom> list;
            if (Objects.nonNull(query.getPage()) && Objects.nonNull(query.getSize())) {
                Page<OooRoom> page = listPage(propertyId, query);
                total = page.getTotal();
                list = page.getRecords();
            } else {
                list = list(propertyId, query);
                total = list.size();
            }
            return new ListPagedDTO<>(total, beanMapper.mapList(list, OooRoomDTO.class));
        }

        /**
         * 关闭 ooo room
         *
         * @param oooRoom ooo room
         * @param userId  操作人
         * @author yyt
         * @date 2026/1/19
         **/
        @Override
        public void close(OooRoom oooRoom, int userId) {
            PropertyDTO propertyDTO = propertyI18nRedisService.get(oooRoom.getPropertyId());
            OooRoom updateContent = new OooRoom();
            updateContent.setId(oooRoom.getId());

            // 已经完成的变成未完成
            boolean isClosed = oooRoom.getCompletedDate() > 0;
            long completedDate = isClosed ? BaseConstant.LONG_ZERO : DateTimeUtils.dateNowTimestamp(propertyDTO.getTimezone());
            updateContent.setCompletedDate(completedDate);
            updateContent.setCompletedBy(isClosed ? BaseConstant.ZERO : userId);
            updateContent.setCompletedTime(isClosed ? BaseConstant.LONG_ZERO : DateTimeUtils.nowTimestamp());

            // 重置 唯一 key
            oooRoom.setCompletedDate(completedDate);
            oooRoom.setUniqKey();
            updateContent.setUniqKeyForSync(oooRoom.getUniqKeyForSync());
            updateById(updateContent);
        }

    }