# BudgetDatabaseConfigOtherTabServiceImpl.java

    package cloud.wconnect.rateshop.service.budgetdatabase.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.exception.ForbiddenException;
    import cloud.wconnect.commonlib.util.DateTimeUtils;
    import cloud.wconnect.commonlib.util.I18nMessageUtils;
    import cloud.wconnect.rateshop.entity.budgetdatabase.BudgetDatabaseConfigOtherTab;
    import cloud.wconnect.rateshop.entity.budgetdatabase.BudgetDatabaseConfigOtherTabItem;
    import cloud.wconnect.rateshop.entity.budgetdatabase.BudgetDatabaseOtherRevenue;
    import cloud.wconnect.rateshop.mapper.budgetdatabase.BudgetDatabaseConfigOtherTabMapper;
    import cloud.wconnect.rateshop.pojo.dto.budgetdatabase.ConfigDeleteFlagDTO;
    import cloud.wconnect.rateshop.pojo.dto.budgetdatabase.SaveTabDTO;
    import cloud.wconnect.rateshop.pojo.dto.budgetdatabase.TabDTO;
    import cloud.wconnect.rateshop.service.budgetdatabase.IBudgetDatabaseConfigOtherTabItemService;
    import cloud.wconnect.rateshop.service.budgetdatabase.IBudgetDatabaseConfigOtherTabService;
    import cloud.wconnect.rateshop.service.budgetdatabase.IBudgetDatabaseConfigVariableOtherService;
    import cloud.wconnect.rateshop.service.budgetdatabase.IBudgetDatabaseOtherRevenueService;
    import com.baomidou.dynamic.datasource.annotation.DS;
    import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.google.common.collect.Lists;
    import org.apache.commons.collections.CollectionUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Lazy;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;

    import java.util.Collections;
    import java.util.List;
    import java.util.Map;
    import java.util.Objects;
    import java.util.stream.Collectors;

    /**
     * <p>
     *  服务实现类
     * </p>
     *
     * @author yyt
     * @since 2025-11-10
     */
    @Service
    @DS("rate_shop")
    public class BudgetDatabaseConfigOtherTabServiceImpl extends ServiceImpl<BudgetDatabaseConfigOtherTabMapper, BudgetDatabaseConfigOtherTab> implements IBudgetDatabaseConfigOtherTabService {

        @Autowired
        private IBudgetDatabaseConfigVariableOtherService budgetDatabaseConfigVariableOtherService;
        @Lazy
        @Autowired
        private IBudgetDatabaseConfigOtherTabItemService budgetDatabaseConfigOtherTabItemService;
        @Lazy
        @Autowired
        private IBudgetDatabaseOtherRevenueService budgetDatabaseOtherRevenueService;
        @Autowired
        private BeanMapper beanMapper;
        @Autowired
        private I18nMessageUtils i18nMessageUtils;

        /**
         * 查询所有 tab
         *
         * @param propertyId 资产 id
         * @param year       年
         * @return tab
         * @author yyt
         * @date 2025/11/10
         **/
        @Override
        public List<BudgetDatabaseConfigOtherTab> list(int propertyId, int year) {
            return list(new QueryWrapper<BudgetDatabaseConfigOtherTab>().lambda()
                    .eq(BudgetDatabaseConfigOtherTab::getPropertyId, propertyId)
                    .eq(BudgetDatabaseConfigOtherTab::getBudgetYear, year)
                    .orderByAsc(BudgetDatabaseConfigOtherTab::getId));
        }

        /**
         * 查询所有 tab
         *
         * @param propertyId 资产 id
         * @param year       年
         * @return tab
         * @author yyt
         * @date 2025/11/10
         **/
        @Override
        public List<TabDTO> listDTOs(int propertyId, int year) {
            List<BudgetDatabaseConfigOtherTab> tabs = list(propertyId, year);
            return beanMapper.mapList(tabs, TabDTO.class);
        }

        /**
         * 验重
         *
         * @param propertyId 资产 id
         * @param year       年
         * @param name       名称
         * @param id         排除自身
         * @author yyt
         * @date 2025/11/10
         **/
        @Override
        public void checkRepeat(int propertyId, int year, String name, Integer id) {
            BudgetDatabaseConfigOtherTab one = getOne(new QueryWrapper<BudgetDatabaseConfigOtherTab>().lambda()
                    .eq(BudgetDatabaseConfigOtherTab::getPropertyId, propertyId)
                    .eq(BudgetDatabaseConfigOtherTab::getBudgetYear, year)
                    .eq(BudgetDatabaseConfigOtherTab::getName, name)
                    .ne(Objects.nonNull(id), BudgetDatabaseConfigOtherTab::getId, id)
                    .last("limit 1"));
            if (Objects.nonNull(one)) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_EXISTED,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "the tab is repeat");
            }
        }

        /**
         * 保存 tab
         *
         * @param propertyId 资产 id
         * @param year       年
         * @param saveTabDTO tab 信息
         * @param userId     操作人
         * @return tab id
         * @author yyt
         * @date 2025/11/10
         **/
        @Override
        public Integer save(int propertyId, int year, SaveTabDTO saveTabDTO, int userId) {
            BudgetDatabaseConfigOtherTab tab = new BudgetDatabaseConfigOtherTab()
                    .setPropertyId(propertyId)
                    .setBudgetYear(year)
                    .setName(saveTabDTO.getName())
                    .setParticipateCalculate(true)
                    .setUpdatedBy(userId)
                    .setUpdatedTime(DateTimeUtils.nowTimestamp());
            save(tab);
            return tab.getId();
        }

        /**
         * 验证存在
         *
         * @param id tab id
         * @return tab
         * @author yyt
         * @date 2025/11/10
         **/
        @Override
        public BudgetDatabaseConfigOtherTab checkExist(int id) {
            BudgetDatabaseConfigOtherTab tab = getById(id);
            if (Objects.isNull(tab)) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_NOT_FOUND,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "the tab is not found");
            }
            return tab;
        }

        /**
         * 校验数据归属
         *
         * @param tab        tab
         * @param propertyId 资产id
         * @author yyt
         * @date 2025/11/10
         */
        @Override
        public void checkBelong(BudgetDatabaseConfigOtherTab tab, int propertyId) {
            if (!tab.getPropertyId().equals(propertyId)) {
                throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                        BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(), "the property id parameter is different from " +
                        "the property id of the budgetDatabaseConfigOtherTab;propertyId:" + propertyId + ",budgetDatabaseConfigOtherTabPropertyId:" +
                        tab.getPropertyId() + ",budgetDatabaseConfigOtherTabId:" + tab.getId());
            }
        }

        /**
         * 修改 tab
         *
         * @param tabId      tab id
         * @param saveTabDTO tab 信息
         * @param userId     操作人
         * @author yyt
         * @date 2025/11/10
         **/
        @Override
        public void update(int tabId, SaveTabDTO saveTabDTO, int userId) {
            updateById(new BudgetDatabaseConfigOtherTab()
                    .setId(tabId)
                    .setName(saveTabDTO.getName())
                    .setUpdatedBy(userId)
                    .setUpdatedTime(DateTimeUtils.nowTimestamp()));
        }

        /**
         * 验证 tab 数量
         *
         * @param propertyId 资产 id
         * @param year       年
         * @author yyt
         * @date 2025/11/10
         **/
        @Override
        public void checkTabCount(int propertyId, int year) {
            // 验证 tab 数量最多不可超过5个
            long count = count(new QueryWrapper<BudgetDatabaseConfigOtherTab>().lambda()
                    .eq(BudgetDatabaseConfigOtherTab::getPropertyId, propertyId)
                    .eq(BudgetDatabaseConfigOtherTab::getBudgetYear, year));
            if (count >= 5) {
                throw new BadRequestException(CommonErrorI18nConstant.CREATE_DATA_LIMIT,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "Creating data exceeds the quantity limit");
            }
        }

        /**
         * 参与计算 Total Revenue
         *
         * @param id                   tab id
         * @param participateCalculate 参与计算
         * @param userId               操作人
         * @author yyt
         * @date 2025/11/11
         **/
        @Override
        public void participateCalculate(int id, boolean participateCalculate, int userId) {
            updateById(new BudgetDatabaseConfigOtherTab().setId(id).setParticipateCalculate(participateCalculate)
                    .setUpdatedBy(userId).setUpdatedTime(DateTimeUtils.nowTimestamp()));
        }

        /**
         * 删除 tab
         *
         * @param tab room tab
         * @return 配置删除标志
         * @author yyt
         * @date 2025/11/11
         **/
        @Override
        @Transactional(rollbackFor = Exception.class)
        public ConfigDeleteFlagDTO handleDelete(BudgetDatabaseConfigOtherTab tab) {
            // 当前 tab 下的指标被变量配置占用时，不可以删除。
            Integer tabId = tab.getId();
            if (budgetDatabaseConfigVariableOtherService.checkTabBoundToTheVariable(tabId)) {
                return new ConfigDeleteFlagDTO(false, true);
            }

            List<BudgetDatabaseOtherRevenue> revenues = Collections.emptyList();
            // 当前 tab 下的指标有数据时，不能删除。
            List<BudgetDatabaseConfigOtherTabItem> itemList = budgetDatabaseConfigOtherTabItemService.listByTabId(tabId);
            if (CollectionUtils.isNotEmpty(itemList)) {
                revenues = budgetDatabaseOtherRevenueService.listByItemIds(itemList.stream().map(BudgetDatabaseConfigOtherTabItem::getId).collect(Collectors.toList()));
                if (CollectionUtils.isNotEmpty(revenues) && revenues.stream().anyMatch(BudgetDatabaseOtherRevenue::hasData)) {
                    return new ConfigDeleteFlagDTO(true, false);
                }
            }

            // 删除 tab
            removeById(tab.getId());

            // 删除 tab 下的指标
            if (CollectionUtils.isNotEmpty(itemList)) {
                budgetDatabaseConfigOtherTabItemService.removeBatchByIds(itemList);
            }

            // 删除 指标占用数据记录
            if (CollectionUtils.isNotEmpty(revenues)) {
                budgetDatabaseOtherRevenueService.removeByIds(revenues.stream().map(BudgetDatabaseOtherRevenue::getId).toList());
            }

            return new ConfigDeleteFlagDTO(false, false);
        }

        /**
         * 批量查询
         *
         * @param tabIds tab ids
         * @param year   年
         * @return tab
         * @author yyt
         * @date 2025/11/11
         **/
        @Override
        public List<BudgetDatabaseConfigOtherTab> list(List<Integer> tabIds, int year) {
            return list(new QueryWrapper<BudgetDatabaseConfigOtherTab>().lambda()
                    .in(BudgetDatabaseConfigOtherTab::getId, tabIds)
                    .eq(BudgetDatabaseConfigOtherTab::getBudgetYear, year));
        }

        /**
         * 验证存在
         *
         * @param propertyId 资产 id
         * @param tabId      tab id
         * @author yyt
         * @date 2025/11/11
         */
        @Override
        public void checkExist(int propertyId, Integer tabId) {
            BudgetDatabaseConfigOtherTab tab = getOne(new QueryWrapper<BudgetDatabaseConfigOtherTab>().lambda()
                    .eq(BudgetDatabaseConfigOtherTab::getPropertyId, propertyId)
                    .eq(BudgetDatabaseConfigOtherTab::getId, tabId));
            if (Objects.isNull(tab)) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_NOT_EXIST,
                        i18nMessageUtils.getErrorParamMapNotNeedTranslate("other tab"),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "other fb tab is not found");
            }
        }

        /**
         * 检查 tab
         *
         * @param tabId      tab id
         * @param propertyId 资产 id
         * @return tab
         * @author yyt
         * @date 2025/11/11
         */
        @Override
        public BudgetDatabaseConfigOtherTab checkTab(int tabId, int propertyId) {
            BudgetDatabaseConfigOtherTab tab = checkExist(tabId);
            checkBelong(tab, propertyId);
            return tab;
        }

        /**
         * 复制上一年的配置至下一年
         *
         * @param propertyId   资产 id
         * @param previousYear 上一年
         * @param nextYear     下一年
         * @return 下一年的配置
         * @author yyt
         * @date 2025/11/14
         **/
        @Override
        @Transactional(rollbackFor = Exception.class)
        public List<BudgetDatabaseConfigOtherTabItem> copy(Integer propertyId, int previousYear, int nextYear) {
            // 查询上一年的配置
            List<BudgetDatabaseConfigOtherTab> tabs = list(propertyId, previousYear);
            if (CollectionUtils.isEmpty(tabs)) return Collections.emptyList();

            List<BudgetDatabaseConfigOtherTabItem> tabItems = budgetDatabaseConfigOtherTabItemService.listByTabIds(tabs.stream().map(BudgetDatabaseConfigOtherTab::getId).toList());
            Map<Integer, List<BudgetDatabaseConfigOtherTabItem>> itemsByTabId = tabItems
                    .stream().collect(Collectors.groupingBy(BudgetDatabaseConfigOtherTabItem::getOtherTabId));

            long now = DateTimeUtils.nowTimestamp();

            List<BudgetDatabaseConfigOtherTab> newTabs = tabs.stream().map(item -> {
                BudgetDatabaseConfigOtherTab tab = beanMapper.map(item, BudgetDatabaseConfigOtherTab.class);
                tab.setBudgetYear(nextYear);
                tab.setId(null);
                tab.setOldTabId(item.getId());
                tab.setUpdatedBy(BaseConstant.ZERO);
                tab.setUpdatedTime(now);
                return tab;
            }).toList();
            saveBatch(newTabs);

            Map<Integer, Integer> newTabIdByOldTabId = newTabs.stream().collect(Collectors.toMap(BudgetDatabaseConfigOtherTab::getOldTabId, BudgetDatabaseConfigOtherTab::getId));

            if (CollectionUtils.isNotEmpty(tabItems)) {
                List<BudgetDatabaseConfigOtherTabItem> newTabItems = Lists.newArrayListWithExpectedSize(tabItems.size());
                itemsByTabId.forEach((tabId, items) -> {

                    Integer newTabId = newTabIdByOldTabId.get(tabId);

                    newTabItems.addAll(items.stream().map(item -> {
                        BudgetDatabaseConfigOtherTabItem newItem = beanMapper.map(item, BudgetDatabaseConfigOtherTabItem.class);
                        newItem.setId(null);
                        newItem.setOtherTabId(newTabId);

                        newItem.setOldTabId(tabId);
                        newItem.setOldItemId(item.getId());
                        newItem.setUpdatedBy(BaseConstant.ZERO);
                        newItem.setUpdatedTime(now);
                        return newItem;
                    }).toList());
                });
                budgetDatabaseConfigOtherTabItemService.saveBatch(newTabItems);

                return newTabItems;
            }

            return Collections.emptyList();
        }

        /**
         * 验证是否有配置
         *
         * @param propertyId 资产 id
         * @param year       年
         * @return 是否有配置
         * @author yyt
         * @date 2025/11/14
         **/
        @Override
        public boolean checkHasConfig(int propertyId, int year) {
            return count(new QueryWrapper<BudgetDatabaseConfigOtherTab>().lambda()
                    .eq(BudgetDatabaseConfigOtherTab::getPropertyId, propertyId)
                    .eq(BudgetDatabaseConfigOtherTab::getBudgetYear, year)) > 0;
        }

        /**
         * 批量查询参与计算的 tab
         *
         * @param propertyId 资产 id
         * @param year       年
         * @return tab
         * @author yyt
         * @date 2025/11/13
         **/
        @Override
        public List<Integer> listParticipateCalculateTabs(int propertyId, int year) {
            return list(new QueryWrapper<BudgetDatabaseConfigOtherTab>().lambda()
                    .in(BudgetDatabaseConfigOtherTab::getPropertyId, propertyId)
                    .eq(BudgetDatabaseConfigOtherTab::getBudgetYear, year)
                    .eq(BudgetDatabaseConfigOtherTab::getParticipateCalculate, true))
                    .stream().map(BudgetDatabaseConfigOtherTab::getId).toList();
        }

    }