# ShiftDrawerCountServiceImpl.java

    package cloud.wconnect.frontoffice.service.drawercount.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.cons.CurrencyEnum;
    import cloud.wconnect.commonlib.exception.BadRequestException;
    import cloud.wconnect.commonlib.exception.ForbiddenException;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.service.UserInternalService;
    import cloud.wconnect.commonlib.util.DateTimeUtils;
    import cloud.wconnect.commonlib.util.I18nMessageUtils;
    import cloud.wconnect.commonlib.util.JsonUtils;
    import cloud.wconnect.frontoffice.cons.drawercount.DrawerCountCons;
    import cloud.wconnect.frontoffice.cons.drawercount.DrawerCountErrorI18nConstant;
    import cloud.wconnect.frontoffice.cons.drawercount.DrawerCountFieldI18nConstant;
    import cloud.wconnect.frontoffice.cons.endofshift.ShiftEnum;
    import cloud.wconnect.frontoffice.entity.drawercount.*;
    import cloud.wconnect.frontoffice.mapper.drawercount.ShiftDrawerCountMapper;
    import cloud.wconnect.frontoffice.pojo.dto.drawercount.*;
    import cloud.wconnect.frontoffice.pojo.dto.drawercount.currency.ShiftDrawerDTO;
    import cloud.wconnect.frontoffice.pojo.dto.drawercount.currency.aud.ShiftDrawerAUDDTO;
    import cloud.wconnect.frontoffice.pojo.dto.drawercount.currency.aud.ShiftDrawerCoinsAUDDTO;
    import cloud.wconnect.frontoffice.pojo.dto.drawercount.currency.aud.ShiftDrawerPaperAUDDTO;
    import cloud.wconnect.frontoffice.pojo.dto.drawercount.currency.cad.ShiftDrawerCADDTO;
    import cloud.wconnect.frontoffice.pojo.dto.drawercount.currency.cad.ShiftDrawerCoinsCADDTO;
    import cloud.wconnect.frontoffice.pojo.dto.drawercount.currency.cad.ShiftDrawerPaperCADDTO;
    import cloud.wconnect.frontoffice.pojo.dto.drawercount.currency.cny.ShiftDrawerCNYDTO;
    import cloud.wconnect.frontoffice.pojo.dto.drawercount.currency.cny.ShiftDrawerCoinsCNYDTO;
    import cloud.wconnect.frontoffice.pojo.dto.drawercount.currency.cny.ShiftDrawerPaperCNYDTO;
    import cloud.wconnect.frontoffice.pojo.dto.drawercount.currency.jpy.ShiftDrawerCoinsJPYDTO;
    import cloud.wconnect.frontoffice.pojo.dto.drawercount.currency.jpy.ShiftDrawerJPYDTO;
    import cloud.wconnect.frontoffice.pojo.dto.drawercount.currency.jpy.ShiftDrawerPaperJPYDTO;
    import cloud.wconnect.frontoffice.pojo.dto.drawercount.currency.thb.ShiftDrawerCoinsTHBDTO;
    import cloud.wconnect.frontoffice.pojo.dto.drawercount.currency.thb.ShiftDrawerPaperTHBDTO;
    import cloud.wconnect.frontoffice.pojo.dto.drawercount.currency.thb.ShiftDrawerTHBDTO;
    import cloud.wconnect.frontoffice.pojo.dto.drawercount.currency.twd.ShiftDrawerCoinsTWDDTO;
    import cloud.wconnect.frontoffice.pojo.dto.drawercount.currency.twd.ShiftDrawerPaperTWDDTO;
    import cloud.wconnect.frontoffice.pojo.dto.drawercount.currency.twd.ShiftDrawerTWDDTO;
    import cloud.wconnect.frontoffice.pojo.dto.drawercount.currency.usd.ShiftDrawerCoinsUSDDTO;
    import cloud.wconnect.frontoffice.pojo.dto.drawercount.currency.usd.ShiftDrawerPaperUSDDTO;
    import cloud.wconnect.frontoffice.pojo.dto.drawercount.currency.usd.ShiftDrawerUSDDTO;
    import cloud.wconnect.frontoffice.service.drawercount.*;
    import com.baomidou.dynamic.datasource.annotation.DS;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.google.common.collect.Lists;
    import com.google.common.collect.Sets;
    import org.apache.commons.collections.CollectionUtils;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;

    import java.math.BigDecimal;
    import java.util.List;
    import java.util.Map;
    import java.util.Objects;
    import java.util.Set;
    import java.util.stream.Collectors;

    /**
     * <p>
     * 服务实现类
     * </p>
     *
     * @author lichen
     * @since 2023-03-27
     */
    @Service
    @DS("ratelog_manage")
    public class ShiftDrawerCountServiceImpl extends ServiceImpl<ShiftDrawerCountMapper, ShiftDrawerCount> implements IShiftDrawerCountService {

        @Autowired
        private ShiftDrawerCountMapper shiftDrawerCountMapper;
        @Autowired
        private BeanMapper beanMapper;
        @Autowired
        private ObjectMapper objectMapper;
        @Autowired
        private IShiftDrawerCommentService shiftDrawerCommentService;
        @Autowired
        private IDrawerCountAmountConfigService drawerCountAmountConfigService;
        @Autowired
        private IDrawerCountNumberConfigService drawerCountNumberConfigService;
        @Autowired
        private IDrawerCountCurrencyConfigService drawerCountCurrencyConfigService;
        @Autowired
        private UserInternalService userInternalService;
        @Autowired
        private I18nMessageUtils i18nMessageUtils;

        /**
         * 查询 Drawer Count 列表
         *
         * @param propertyDTO 资产DTO
         * @param queryTimes  查询时间
         * @return ShiftDrawerCountDTO 集合
         * @author zc
         * @date 2023/8/25
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public List<ShiftDrawerCountDTO> list(PropertyDTO propertyDTO, List<Long> queryTimes) {
            Integer propertyId = propertyDTO.getId();
            // 查询 Drawer Count 数据
            List<ShiftDrawerCount> shiftDrawerCountList = shiftDrawerCountMapper.selectForUpdate(propertyId, queryTimes);

            // 查询数据的日期数量 小于 查询条件日期数量时，表示有对应日期的数据未生成，这部分数据需要初始化出来
            Set<Long> targetDateList = shiftDrawerCountList.stream().map(ShiftDrawerCount::getTargetDate).collect(Collectors.toSet());
            if (targetDateList.size() < queryTimes.size()) {
                shiftDrawerCountList.addAll(initShiftDrawerCount(propertyId, shiftDrawerCountList, queryTimes));
            }

            // 提取操作人名称
            Map<Integer, String> userId2Name = extractOperatorName(shiftDrawerCountList, propertyDTO.getTenantId());

            // 对 Drawer Count List 分组处理
            Map<Long, Map<Integer, List<ShiftDrawerCount>>> shift2ShiftDrawerCountListByTargetDate = shiftDrawerCountList.stream()
                    .collect(Collectors.groupingBy(ShiftDrawerCount::getTargetDate, Collectors.groupingBy(ShiftDrawerCount::getShift)));

            // 组装返回数据
            List<ShiftDrawerCountDTO> shiftDrawerCountDTOList = Lists.newArrayList();
            shift2ShiftDrawerCountListByTargetDate.forEach((targetDate, maps) ->
                    maps.forEach((shift, values) -> {
                        ShiftDrawerCountDTO shiftDrawerCountDTO = new ShiftDrawerCountDTO();
                        shiftDrawerCountDTO.setTargetDate(targetDate);
                        shiftDrawerCountDTO.setShift(shift);
                        shiftDrawerCountDTO.setDrawers(values.stream()
                                .map(item -> generateShiftDrawers(item, userId2Name)).collect(Collectors.toList()));
                        shiftDrawerCountDTO.setDrawerCurrency(values.stream()
                                .map(ShiftDrawerCount::getDrawerCurrency).findFirst().get());
                        shiftDrawerCountDTO.setDrawerInit(values.stream()
                                .map(ShiftDrawerCount::getDrawerInit).findFirst().get());

                        shiftDrawerCountDTOList.add(shiftDrawerCountDTO);
                    }));
            return shiftDrawerCountDTOList;
        }

        /**
         * 生成 Shift Drawers
         *
         * @param shiftDrawerCount Shift Drawer Count 数据
         * @param userId2Name      userId 2 name
         * @return ShiftDrawersDTO
         * @author zc
         * @date 2023/8/25
         */
        private ShiftDrawersDTO generateShiftDrawers(ShiftDrawerCount shiftDrawerCount, Map<Integer, String> userId2Name) {
            ShiftDrawersDTO shiftDrawer = new ShiftDrawersDTO();
            shiftDrawer.setId(shiftDrawerCount.getId());
            shiftDrawer.setDrawerIn(generateShiftDrawer(userId2Name, shiftDrawerCount.getDrawerIn(), shiftDrawerCount.getDrawerCurrency()));
            shiftDrawer.setDrawerOut(generateShiftDrawer(userId2Name, shiftDrawerCount.getDrawerOut(), shiftDrawerCount.getDrawerCurrency()));

            ShiftDrawerWitnessDTO witness = new ShiftDrawerWitnessDTO();
            if (shiftDrawerCount.getWitnessBy() != BaseConstant.ZERO) {
                witness.setWitnessName(userId2Name.get(shiftDrawerCount.getWitnessBy()));
                witness.setWitnessBy(shiftDrawerCount.getWitnessBy());
                witness.setWitnessAuditTime(shiftDrawerCount.getWitnessAuditTime());
            }
            shiftDrawer.setWitness(witness);
            return shiftDrawer;
        }

        /**
         * 生成 ShiftDrawerDTO
         *
         * @param userId2Name    userId 2 name
         * @param drawerContent  drawer#in、#out 的内容
         * @param drawerCurrency 币种
         * @return ShiftDrawerDTO
         * @author zc
         * @date 2023/8/25
         */
        private ShiftDrawerDTO generateShiftDrawer(Map<Integer, String> userId2Name, String drawerContent, int drawerCurrency) {
            if (CurrencyEnum.USD.getId() == drawerCurrency) {
                return generateShiftDrawerForUsd(userId2Name, drawerContent);
            } else if (CurrencyEnum.CNY.getId() == drawerCurrency) {
                return generateShiftDrawerForCny(userId2Name, drawerContent);
            } else if (CurrencyEnum.TWD.getId() == drawerCurrency) {
                return generateShiftDrawerForTwd(userId2Name, drawerContent);
            } else if (CurrencyEnum.JPY.getId() == drawerCurrency) {
                return generateShiftDrawerForJpy(userId2Name, drawerContent);
            } else if (CurrencyEnum.CAD.getId() == drawerCurrency) {
                return generateShiftDrawerForCad(userId2Name, drawerContent);
            } else if (CurrencyEnum.AUD.getId() == drawerCurrency) {
                return generateShiftDrawerForAud(userId2Name, drawerContent);
            } else {
                return generateShiftDrawerForThb(userId2Name, drawerContent);
            }
        }

        /**
         * 生成 Shift Drawer 结构 for THB(泰铢)
         *
         * @param userId2Name   用户姓名集合
         * @param drawerContent json 结构数据
         * @return cloud.wconnect.frontoffice.pojo.dto.drawercount.currency.ShiftDrawerDTO
         * @author lichen
         * @date 2025/5/19
         */
        private ShiftDrawerDTO generateShiftDrawerForThb(Map<Integer, String> userId2Name, String drawerContent) {
            if (StringUtils.isNotEmpty(drawerContent)) {
                ShiftDrawerTHBDTO shiftDrawer = JsonUtils.convertStringToObject(objectMapper, drawerContent, ShiftDrawerTHBDTO.class);
                // 设置 Gsa 的操作人名称
                ShiftDrawerGSADTO gsa = shiftDrawer.getGsa();
                gsa.setGsaName(userId2Name.get(gsa.getGsaBy()));

                return ShiftDrawerDTO.builder()
                        .rolled(JsonUtils.convertObjToString(objectMapper, shiftDrawer.getRolled()))
                        .loose(JsonUtils.convertObjToString(objectMapper, shiftDrawer.getLoose()))
                        .paper(JsonUtils.convertObjToString(objectMapper, shiftDrawer.getPaper()))
                        .pettyCash(shiftDrawer.getPettyCash())
                        .gsa(shiftDrawer.getGsa())
                        .times(Objects.isNull(shiftDrawer.getTimes()) ?
                                BaseConstant.ZERO : shiftDrawer.getTimes()).build();
            } else {
                String shiftDrawerCoins = JsonUtils.convertObjToString(objectMapper, new ShiftDrawerCoinsTHBDTO());
                return ShiftDrawerDTO.builder()
                        .rolled(shiftDrawerCoins)
                        .loose(shiftDrawerCoins)
                        .paper(JsonUtils.convertObjToString(objectMapper, new ShiftDrawerPaperTHBDTO()))
                        .gsa(new ShiftDrawerGSADTO())
                        .times(BaseConstant.ZERO).build();
            }
        }

        /**
         * 生成 Shift Drawer 结构 for AUD
         *
         * @param userId2Name   userId 2 name
         * @param drawerContent json 结构数据
         * @return cloud.wconnect.frontoffice.pojo.dto.drawercount.currency.ShiftDrawerDTO
         * @author lichen
         * @date 2024/10/22
         */
        private ShiftDrawerDTO generateShiftDrawerForAud(Map<Integer, String> userId2Name, String drawerContent) {
            if (StringUtils.isNotEmpty(drawerContent)) {
                ShiftDrawerAUDDTO shiftDrawer = JsonUtils.convertStringToObject(objectMapper, drawerContent, ShiftDrawerAUDDTO.class);
                // 设置 Gsa 的操作人名称
                ShiftDrawerGSADTO gsa = shiftDrawer.getGsa();
                gsa.setGsaName(userId2Name.get(gsa.getGsaBy()));

                return ShiftDrawerDTO.builder()
                        .rolled(JsonUtils.convertObjToString(objectMapper, shiftDrawer.getRolled()))
                        .loose(JsonUtils.convertObjToString(objectMapper, shiftDrawer.getLoose()))
                        .paper(JsonUtils.convertObjToString(objectMapper, shiftDrawer.getPaper()))
                        .pettyCash(shiftDrawer.getPettyCash())
                        .gsa(shiftDrawer.getGsa())
                        .times(Objects.isNull(shiftDrawer.getTimes()) ?
                                BaseConstant.ZERO : shiftDrawer.getTimes()).build();
            } else {
                String shiftDrawerCoins = JsonUtils.convertObjToString(objectMapper, new ShiftDrawerCoinsAUDDTO());
                return ShiftDrawerDTO.builder()
                        .rolled(shiftDrawerCoins)
                        .loose(shiftDrawerCoins)
                        .paper(JsonUtils.convertObjToString(objectMapper, new ShiftDrawerPaperAUDDTO()))
                        .gsa(new ShiftDrawerGSADTO())
                        .times(BaseConstant.ZERO).build();
            }
        }

        /**
         * 生成 Shift Drawer 结构 for USD
         *
         * @param userId2Name   userId 2 name
         * @param drawerContent json 结构数据
         * @return ShiftDrawerDTO
         * @author zc
         * @date 2023/8/26
         */
        private ShiftDrawerDTO generateShiftDrawerForUsd(Map<Integer, String> userId2Name, String drawerContent) {
            if (StringUtils.isNotEmpty(drawerContent)) {
                ShiftDrawerUSDDTO shiftDrawer = JsonUtils.convertStringToObject(objectMapper, drawerContent, ShiftDrawerUSDDTO.class);
                // 设置 Gsa 的操作人名称
                ShiftDrawerGSADTO gsa = shiftDrawer.getGsa();
                gsa.setGsaName(userId2Name.get(gsa.getGsaBy()));

                return ShiftDrawerDTO.builder()
                        .rolled(JsonUtils.convertObjToString(objectMapper, shiftDrawer.getRolled()))
                        .loose(JsonUtils.convertObjToString(objectMapper, shiftDrawer.getLoose()))
                        .paper(JsonUtils.convertObjToString(objectMapper, shiftDrawer.getPaper()))
                        .pettyCash(shiftDrawer.getPettyCash())
                        .gsa(shiftDrawer.getGsa())
                        .times(Objects.isNull(shiftDrawer.getTimes()) ?
                                BaseConstant.ZERO : shiftDrawer.getTimes()).build();
            } else {
                String shiftDrawerCoins = JsonUtils.convertObjToString(objectMapper, new ShiftDrawerCoinsUSDDTO());
                return ShiftDrawerDTO.builder()
                        .rolled(shiftDrawerCoins)
                        .loose(shiftDrawerCoins)
                        .paper(JsonUtils.convertObjToString(objectMapper, new ShiftDrawerPaperUSDDTO()))
                        .gsa(new ShiftDrawerGSADTO())
                        .times(BaseConstant.ZERO).build();
            }
        }

        /**
         * 生成 Shift Drawer 结构 for CNY
         *
         * @param userId2Name   userId 2 name
         * @param drawerContent json 结构数据
         * @return ShiftDrawerDTO
         * @author zc
         * @date 2023/8/26
         */
        private ShiftDrawerDTO generateShiftDrawerForCny(Map<Integer, String> userId2Name, String drawerContent) {
            if (StringUtils.isNotEmpty(drawerContent)) {
                ShiftDrawerCNYDTO shiftDrawer = JsonUtils.convertStringToObject(objectMapper, drawerContent, ShiftDrawerCNYDTO.class);
                // 设置 Gsa 的操作人名称
                ShiftDrawerGSADTO gsa = shiftDrawer.getGsa();
                gsa.setGsaName(userId2Name.get(gsa.getGsaBy()));

                return ShiftDrawerDTO.builder()
                        .rolled(JsonUtils.convertObjToString(objectMapper, shiftDrawer.getRolled()))
                        .loose(JsonUtils.convertObjToString(objectMapper, shiftDrawer.getLoose()))
                        .paper(JsonUtils.convertObjToString(objectMapper, shiftDrawer.getPaper()))
                        .pettyCash(shiftDrawer.getPettyCash())
                        .gsa(shiftDrawer.getGsa())
                        .times(Objects.isNull(shiftDrawer.getTimes()) ?
                                BaseConstant.ZERO : shiftDrawer.getTimes()).build();
            } else {
                String shiftDrawerCoins = JsonUtils.convertObjToString(objectMapper, new ShiftDrawerCoinsCNYDTO());
                return ShiftDrawerDTO.builder()
                        .rolled(shiftDrawerCoins)
                        .loose(shiftDrawerCoins)
                        .paper(JsonUtils.convertObjToString(objectMapper, new ShiftDrawerPaperCNYDTO()))
                        .gsa(new ShiftDrawerGSADTO())
                        .times(BaseConstant.ZERO).build();
            }
        }

        /**
         * 生成 Shift Drawer 结构 for TWD
         *
         * @param userId2Name   userId 2 name
         * @param drawerContent json 结构数据
         * @return ShiftDrawerDTO
         * @author zc
         * @date 2023/8/26
         */
        private ShiftDrawerDTO generateShiftDrawerForTwd(Map<Integer, String> userId2Name, String drawerContent) {
            if (StringUtils.isNotEmpty(drawerContent)) {
                ShiftDrawerTWDDTO shiftDrawer = JsonUtils.convertStringToObject(objectMapper, drawerContent, ShiftDrawerTWDDTO.class);
                // 设置 Gsa 的操作人名称
                ShiftDrawerGSADTO gsa = shiftDrawer.getGsa();
                gsa.setGsaName(userId2Name.get(gsa.getGsaBy()));

                return ShiftDrawerDTO.builder()
                        .rolled(JsonUtils.convertObjToString(objectMapper, shiftDrawer.getRolled()))
                        .loose(JsonUtils.convertObjToString(objectMapper, shiftDrawer.getLoose()))
                        .paper(JsonUtils.convertObjToString(objectMapper, shiftDrawer.getPaper()))
                        .pettyCash(shiftDrawer.getPettyCash())
                        .gsa(shiftDrawer.getGsa())
                        .times(Objects.isNull(shiftDrawer.getTimes()) ?
                                BaseConstant.ZERO : shiftDrawer.getTimes()).build();
            } else {
                String shiftDrawerCoins = JsonUtils.convertObjToString(objectMapper, new ShiftDrawerCoinsTWDDTO());
                return ShiftDrawerDTO.builder()
                        .rolled(shiftDrawerCoins)
                        .loose(shiftDrawerCoins)
                        .paper(JsonUtils.convertObjToString(objectMapper, new ShiftDrawerPaperTWDDTO()))
                        .gsa(new ShiftDrawerGSADTO())
                        .times(BaseConstant.ZERO).build();
            }
        }

        /**
         * 生成 Shift Drawer 结构 for JPY
         *
         * @param userId2Name   userId 2 name
         * @param drawerContent json 结构数据
         * @return ShiftDrawerDTO
         * @author zc
         * @date 2023/8/26
         */
        private ShiftDrawerDTO generateShiftDrawerForJpy(Map<Integer, String> userId2Name, String drawerContent) {
            if (StringUtils.isNotEmpty(drawerContent)) {
                ShiftDrawerJPYDTO shiftDrawer = JsonUtils.convertStringToObject(objectMapper, drawerContent, ShiftDrawerJPYDTO.class);
                // 设置 Gsa 的操作人名称
                ShiftDrawerGSADTO gsa = shiftDrawer.getGsa();
                gsa.setGsaName(userId2Name.get(gsa.getGsaBy()));

                return ShiftDrawerDTO.builder()
                        .rolled(JsonUtils.convertObjToString(objectMapper, shiftDrawer.getRolled()))
                        .loose(JsonUtils.convertObjToString(objectMapper, shiftDrawer.getLoose()))
                        .paper(JsonUtils.convertObjToString(objectMapper, shiftDrawer.getPaper()))
                        .pettyCash(shiftDrawer.getPettyCash())
                        .gsa(shiftDrawer.getGsa())
                        .times(Objects.isNull(shiftDrawer.getTimes()) ?
                                BaseConstant.ZERO : shiftDrawer.getTimes()).build();
            } else {
                String shiftDrawerCoins = JsonUtils.convertObjToString(objectMapper, new ShiftDrawerCoinsJPYDTO());
                return ShiftDrawerDTO.builder()
                        .rolled(shiftDrawerCoins)
                        .loose(shiftDrawerCoins)
                        .paper(JsonUtils.convertObjToString(objectMapper, new ShiftDrawerPaperJPYDTO()))
                        .gsa(new ShiftDrawerGSADTO())
                        .times(BaseConstant.ZERO).build();
            }
        }

        /**
         * 生成 Shift Drawer 结构 for CAD
         *
         * @param userId2Name   userId 2 name
         * @param drawerContent json 结构数据
         * @return ShiftDrawerDTO
         * @author zc
         * @date 2024/9/27
         */
        private ShiftDrawerDTO generateShiftDrawerForCad(Map<Integer, String> userId2Name, String drawerContent) {
            if (StringUtils.isNotEmpty(drawerContent)) {
                ShiftDrawerCADDTO shiftDrawer = JsonUtils.convertStringToObject(objectMapper, drawerContent, ShiftDrawerCADDTO.class);
                // 设置 Gsa 的操作人名称
                ShiftDrawerGSADTO gsa = shiftDrawer.getGsa();
                gsa.setGsaName(userId2Name.get(gsa.getGsaBy()));

                return ShiftDrawerDTO.builder()
                        .rolled(JsonUtils.convertObjToString(objectMapper, shiftDrawer.getRolled()))
                        .loose(JsonUtils.convertObjToString(objectMapper, shiftDrawer.getLoose()))
                        .paper(JsonUtils.convertObjToString(objectMapper, shiftDrawer.getPaper()))
                        .pettyCash(shiftDrawer.getPettyCash())
                        .gsa(shiftDrawer.getGsa())
                        .times(Objects.isNull(shiftDrawer.getTimes()) ?
                                BaseConstant.ZERO : shiftDrawer.getTimes()).build();
            } else {
                String shiftDrawerCoins = JsonUtils.convertObjToString(objectMapper, new ShiftDrawerCoinsCADDTO());
                return ShiftDrawerDTO.builder()
                        .rolled(shiftDrawerCoins)
                        .loose(shiftDrawerCoins)
                        .paper(JsonUtils.convertObjToString(objectMapper, new ShiftDrawerPaperCADDTO()))
                        .gsa(new ShiftDrawerGSADTO())
                        .times(BaseConstant.ZERO).build();
            }
        }

        /**
         * 提取操作人名称
         *
         * @param shiftDrawerCountList Drawer count 数据
         * @param tenantId             租户 id
         * @return 操作人名称
         * @author zc
         * @date 2023/8/25
         */
        private Map<Integer, String> extractOperatorName(List<ShiftDrawerCount> shiftDrawerCountList, int tenantId) {
            // 获取 userId 用于查询对应的 userName
            Set<Integer> userIdSet = Sets.newHashSet();
            shiftDrawerCountList.forEach(item -> {
                userIdSet.add(item.getWitnessBy());
                String drawerIn = item.getDrawerIn();
                if (StringUtils.isNotEmpty(drawerIn)) {
                    userIdSet.add(extractGsaIds(item.getDrawerCurrency(), drawerIn));
                }
                String drawerOut = item.getDrawerOut();
                if (StringUtils.isNotEmpty(drawerOut)) {
                    userIdSet.add(extractGsaIds(item.getDrawerCurrency(), drawerOut));
                }
            });

            // 去除 userId 为 0 的数据
            userIdSet.remove(BaseConstant.ZERO);
            return userInternalService.getNameMapByIds(tenantId, userIdSet);
        }

        /**
         * 提取 Gsa id
         *
         * @param drawerCurrency 币种
         * @param drawerStr      Drawer Count 数据
         * @return gsa id
         * @author zc
         * @date 2023/8/28
         */
        private int extractGsaIds(int drawerCurrency, String drawerStr) {
            if (drawerCurrency == CurrencyEnum.USD.getId()) {
                return JsonUtils.convertStringToObject(objectMapper, drawerStr, ShiftDrawerUSDDTO.class).getGsa().getGsaBy();
            } else if (drawerCurrency == CurrencyEnum.CNY.getId()) {
                return JsonUtils.convertStringToObject(objectMapper, drawerStr, ShiftDrawerCNYDTO.class).getGsa().getGsaBy();
            } else if (drawerCurrency == CurrencyEnum.TWD.getId()) {
                return JsonUtils.convertStringToObject(objectMapper, drawerStr, ShiftDrawerTWDDTO.class).getGsa().getGsaBy();
            } else if (drawerCurrency == CurrencyEnum.JPY.getId()) {
                return JsonUtils.convertStringToObject(objectMapper, drawerStr, ShiftDrawerJPYDTO.class).getGsa().getGsaBy();
            } else if (drawerCurrency == CurrencyEnum.CAD.getId()) {
                return JsonUtils.convertStringToObject(objectMapper, drawerStr, ShiftDrawerCADDTO.class).getGsa().getGsaBy();
            } else if (drawerCurrency == CurrencyEnum.AUD.getId()) {
                return JsonUtils.convertStringToObject(objectMapper, drawerStr, ShiftDrawerAUDDTO.class).getGsa().getGsaBy();
            } else {
                return JsonUtils.convertStringToObject(objectMapper, drawerStr, ShiftDrawerTHBDTO.class).getGsa().getGsaBy();
            }
        }

        /**
         * 编辑 Drawer Count
         *
         * @param id             Drawer Count 主键id
         * @param propertyId     资产 id
         * @param type           1:in; 2:out
         * @param shiftDrawerDTO 编辑的数据DTO
         * @param loginUserId    登录用户id
         * @param loginUserName  用户姓名
         * @return ShiftDrawerGSADTO
         * @author zc
         * @date 2023/8/26
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public ShiftDrawerGSADTO update(int id, int propertyId, int type, ShiftDrawerDTO shiftDrawerDTO, int loginUserId, String loginUserName) {
            //查询当前编辑的数据
            ShiftDrawerCount oriShiftDrawerCount = shiftDrawerCountMapper.getByIdForUpdate(id);
            //已审计的数据不可编辑
            if (oriShiftDrawerCount.getWitnessAuditTime() > BaseConstant.ZERO) {
                throw new BadRequestException(DrawerCountErrorI18nConstant.DRAWER_COUNT_ALREADY_AUDIT,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "Data that has been audited is not editable");
            }

            int times = 0;
            //校验是否可以修改
            if (DrawerCountCons.DRAWER_IN == type) {
                // drawerOut 提交后 drawerIn 不可编辑
                if (StringUtils.isNotEmpty(oriShiftDrawerCount.getDrawerOut())) {
                    throw new BadRequestException(CommonErrorI18nConstant.CANNOT_MODIFIED,
                            i18nMessageUtils.getErrorParamMapNeedTranslate(DrawerCountFieldI18nConstant.TAKEOVER),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "The current drawer can not be edit.");
                }
                if (StringUtils.isNotEmpty(oriShiftDrawerCount.getDrawerIn())) {
                    times = extractTimes(oriShiftDrawerCount.getDrawerCurrency(), oriShiftDrawerCount.getDrawerIn());
                }
            } else {
                if (StringUtils.isNotEmpty(oriShiftDrawerCount.getDrawerOut())) {
                    times = extractTimes(oriShiftDrawerCount.getDrawerCurrency(), oriShiftDrawerCount.getDrawerOut());
                }
            }
            //编辑次数不能超过3次
            if (times >= DrawerCountCons.MAX_TIMES) {
                throw new BadRequestException(DrawerCountErrorI18nConstant.DRAWER_COUNT_EDIT_COUNT_EXCEED,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "The current drawer cannot edits exceed 3 times");
            }

            //设置当前GSADTO
            ShiftDrawerGSADTO gsa = new ShiftDrawerGSADTO();
            gsa.setGsaBy(loginUserId);
            gsa.setGsaUpdateTime(DateTimeUtils.nowTimestamp());
            gsa.setGsaName(loginUserName);
            shiftDrawerDTO.setGsa(gsa);
            shiftDrawerDTO.setTimes(++times);

            //构建修改对象
            ShiftDrawerCount updateShiftDrawerCount = new ShiftDrawerCount();
            updateShiftDrawerCount.setId(oriShiftDrawerCount.getId());
            String drawerContent = generateDrawerCountStr(oriShiftDrawerCount.getDrawerCurrency(), shiftDrawerDTO);
            if (DrawerCountCons.DRAWER_IN == type) {
                updateShiftDrawerCount.setDrawerIn(drawerContent);
            } else {
                updateShiftDrawerCount.setDrawerOut(drawerContent);
            }
            //执行修改
            updateById(updateShiftDrawerCount);
            return gsa;
        }

        /**
         * 提取 times
         *
         * @param drawerCurrency 币种
         * @param drawerStr      Drawer Count 数据
         * @return gsa id
         * @author zc
         * @date 2023/8/28
         */
        private int extractTimes(int drawerCurrency, String drawerStr) {
            if (drawerCurrency == CurrencyEnum.USD.getId()) {
                return JsonUtils.convertStringToObject(objectMapper, drawerStr, ShiftDrawerUSDDTO.class).getTimes();
            } else if (drawerCurrency == CurrencyEnum.CNY.getId()) {
                return JsonUtils.convertStringToObject(objectMapper, drawerStr, ShiftDrawerCNYDTO.class).getTimes();
            } else if (drawerCurrency == CurrencyEnum.TWD.getId()) {
                return JsonUtils.convertStringToObject(objectMapper, drawerStr, ShiftDrawerTWDDTO.class).getTimes();
            } else if (drawerCurrency == CurrencyEnum.JPY.getId()) {
                return JsonUtils.convertStringToObject(objectMapper, drawerStr, ShiftDrawerJPYDTO.class).getTimes();
            } else if (drawerCurrency == CurrencyEnum.CAD.getId()) {
                return JsonUtils.convertStringToObject(objectMapper, drawerStr, ShiftDrawerCADDTO.class).getTimes();
            } else if (drawerCurrency == CurrencyEnum.AUD.getId()) {
                return JsonUtils.convertStringToObject(objectMapper, drawerStr, ShiftDrawerAUDDTO.class).getTimes();
            } else {
                return JsonUtils.convertStringToObject(objectMapper, drawerStr, ShiftDrawerTHBDTO.class).getTimes();
            }
        }

        /**
         * 转换为 json 格式
         *
         * @param drawerCurrency 币种
         * @param shiftDrawerDTO 编辑的内容
         * @return java.lang.String
         * @author zc
         * @date 2023/8/28
         */
        private String generateDrawerCountStr(int drawerCurrency, ShiftDrawerDTO shiftDrawerDTO) {
            String drawerContent;
            if (drawerCurrency == CurrencyEnum.USD.getId()) {
                drawerContent = JsonUtils.convertObjToString(objectMapper, ShiftDrawerUSDDTO.builder()
                        .rolled(JsonUtils.convertStringToObject(objectMapper, shiftDrawerDTO.getRolled(), ShiftDrawerCoinsUSDDTO.class))
                        .loose(JsonUtils.convertStringToObject(objectMapper, shiftDrawerDTO.getLoose(), ShiftDrawerCoinsUSDDTO.class))
                        .paper(JsonUtils.convertStringToObject(objectMapper, shiftDrawerDTO.getPaper(), ShiftDrawerPaperUSDDTO.class))
                        .gsa(shiftDrawerDTO.getGsa())
                        .pettyCash(shiftDrawerDTO.getPettyCash())
                        .times(shiftDrawerDTO.getTimes())
                        .build());
            } else if (drawerCurrency == CurrencyEnum.CNY.getId()) {
                drawerContent = JsonUtils.convertObjToString(objectMapper, ShiftDrawerCNYDTO.builder()
                        .rolled(JsonUtils.convertStringToObject(objectMapper, shiftDrawerDTO.getRolled(), ShiftDrawerCoinsCNYDTO.class))
                        .loose(JsonUtils.convertStringToObject(objectMapper, shiftDrawerDTO.getLoose(), ShiftDrawerCoinsCNYDTO.class))
                        .paper(JsonUtils.convertStringToObject(objectMapper, shiftDrawerDTO.getPaper(), ShiftDrawerPaperCNYDTO.class))
                        .gsa(shiftDrawerDTO.getGsa())
                        .pettyCash(shiftDrawerDTO.getPettyCash())
                        .times(shiftDrawerDTO.getTimes())
                        .build());
            } else if (drawerCurrency == CurrencyEnum.TWD.getId()) {
                drawerContent = JsonUtils.convertObjToString(objectMapper, ShiftDrawerTWDDTO.builder()
                        .rolled(JsonUtils.convertStringToObject(objectMapper, shiftDrawerDTO.getRolled(), ShiftDrawerCoinsTWDDTO.class))
                        .loose(JsonUtils.convertStringToObject(objectMapper, shiftDrawerDTO.getLoose(), ShiftDrawerCoinsTWDDTO.class))
                        .paper(JsonUtils.convertStringToObject(objectMapper, shiftDrawerDTO.getPaper(), ShiftDrawerPaperTWDDTO.class))
                        .gsa(shiftDrawerDTO.getGsa())
                        .pettyCash(shiftDrawerDTO.getPettyCash())
                        .times(shiftDrawerDTO.getTimes())
                        .build());
            } else if (drawerCurrency == CurrencyEnum.JPY.getId()) {
                drawerContent = JsonUtils.convertObjToString(objectMapper, ShiftDrawerJPYDTO.builder()
                        .rolled(JsonUtils.convertStringToObject(objectMapper, shiftDrawerDTO.getRolled(), ShiftDrawerCoinsJPYDTO.class))
                        .loose(JsonUtils.convertStringToObject(objectMapper, shiftDrawerDTO.getLoose(), ShiftDrawerCoinsJPYDTO.class))
                        .paper(JsonUtils.convertStringToObject(objectMapper, shiftDrawerDTO.getPaper(), ShiftDrawerPaperJPYDTO.class))
                        .gsa(shiftDrawerDTO.getGsa())
                        .pettyCash(shiftDrawerDTO.getPettyCash())
                        .times(shiftDrawerDTO.getTimes())
                        .build());
            } else if (drawerCurrency == CurrencyEnum.CAD.getId()) {
                drawerContent = JsonUtils.convertObjToString(objectMapper, ShiftDrawerCADDTO.builder()
                        .rolled(JsonUtils.convertStringToObject(objectMapper, shiftDrawerDTO.getRolled(), ShiftDrawerCoinsCADDTO.class))
                        .loose(JsonUtils.convertStringToObject(objectMapper, shiftDrawerDTO.getLoose(), ShiftDrawerCoinsCADDTO.class))
                        .paper(JsonUtils.convertStringToObject(objectMapper, shiftDrawerDTO.getPaper(), ShiftDrawerPaperCADDTO.class))
                        .gsa(shiftDrawerDTO.getGsa())
                        .pettyCash(shiftDrawerDTO.getPettyCash())
                        .times(shiftDrawerDTO.getTimes())
                        .build());
            } else if (drawerCurrency == CurrencyEnum.AUD.getId()) {
                drawerContent = JsonUtils.convertObjToString(objectMapper, ShiftDrawerAUDDTO.builder()
                        .rolled(JsonUtils.convertStringToObject(objectMapper, shiftDrawerDTO.getRolled(), ShiftDrawerCoinsAUDDTO.class))
                        .loose(JsonUtils.convertStringToObject(objectMapper, shiftDrawerDTO.getLoose(), ShiftDrawerCoinsAUDDTO.class))
                        .paper(JsonUtils.convertStringToObject(objectMapper, shiftDrawerDTO.getPaper(), ShiftDrawerPaperAUDDTO.class))
                        .gsa(shiftDrawerDTO.getGsa())
                        .pettyCash(shiftDrawerDTO.getPettyCash())
                        .times(shiftDrawerDTO.getTimes())
                        .build());
            } else {
                drawerContent = JsonUtils.convertObjToString(objectMapper, ShiftDrawerTHBDTO.builder()
                        .rolled(JsonUtils.convertStringToObject(objectMapper, shiftDrawerDTO.getRolled(), ShiftDrawerCoinsTHBDTO.class))
                        .loose(JsonUtils.convertStringToObject(objectMapper, shiftDrawerDTO.getLoose(), ShiftDrawerCoinsTHBDTO.class))
                        .paper(JsonUtils.convertStringToObject(objectMapper, shiftDrawerDTO.getPaper(), ShiftDrawerPaperTHBDTO.class))
                        .gsa(shiftDrawerDTO.getGsa())
                        .pettyCash(shiftDrawerDTO.getPettyCash())
                        .times(shiftDrawerDTO.getTimes())
                        .build());
            }
            return drawerContent;
        }

        /**
         * 审核Drawer Count
         *
         * @param id            主键id
         * @param propertyId    资产id
         * @param loginUserId   登录用户id
         * @param loginUserName 用户姓名
         * @return cloud.wconnect.frontoffice.pojo.dto.drawercount.ShiftDrawerWitnessDTO
         * @author lichen
         * @date 2025/8/26
         */
        @Override
        public ShiftDrawerWitnessDTO audit(int id, Integer propertyId, int loginUserId, String loginUserName) {
            //修改Drawer Count
            ShiftDrawerCount updateShiftDrawerCount = new ShiftDrawerCount();
            updateShiftDrawerCount.setId(id);
            updateShiftDrawerCount.setWitnessBy(loginUserId);
            updateShiftDrawerCount.setWitnessAuditTime(DateTimeUtils.nowTimestamp());
            updateById(updateShiftDrawerCount);
            //返回当前操作人以及操作时间
            ShiftDrawerWitnessDTO witness = new ShiftDrawerWitnessDTO();
            witness.setWitnessBy(loginUserId);
            witness.setWitnessAuditTime(DateTimeUtils.nowTimestamp());
            witness.setWitnessName(loginUserName);
            return witness;
        }

        /**
         * 保存comment
         *
         * @param id            主键id
         * @param commentDTO    备注DTO
         * @param loginUserId   登录用户id
         * @param loginUserName 用户姓名
         * @return cloud.wconnect.frontoffice.pojo.dto.drawercount.ShiftDrawerCommentDTO
         * @author lichen
         * @date 2025/8/26
         */
        @Override
        public ShiftDrawerCommentDTO saveComment(int id, ShiftDrawerCommentDTO commentDTO, int loginUserId, String loginUserName) {
            //当前时间戳
            long operatorTime = DateTimeUtils.nowTimestamp();
            //构建comment对象
            ShiftDrawerComment comment = beanMapper.map(commentDTO, ShiftDrawerComment.class, dto -> {
                dto.setDrawerCountId(id);
                dto.setUpdateTime(operatorTime);
            });

            if (comment.getId() == BaseConstant.ZERO) {
                //新增数据
                comment.setDrawerCountId(id);
                comment.setCreateUid(loginUserId);
                shiftDrawerCommentService.save(comment);
            } else {
                Integer createUid = shiftDrawerCommentService.getById(comment.getId()).getCreateUid();
                if (!createUid.equals(loginUserId)) {
                    throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                            BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(), "Only the creator can update");
                }
                //更新数据
                shiftDrawerCommentService.updateById(comment);
            }
            commentDTO.setId(comment.getId());
            commentDTO.setCreateUid(loginUserId);
            commentDTO.setUserName(loginUserName);
            commentDTO.setUpdateTime(operatorTime);
            return commentDTO;
        }

        /**
         * 初始化 Drawer Count
         *
         * @param propertyId 资产id
         * @return ShiftDrawerCount 集合
         * @author zc
         * @date 2023/8/25
         */
        @Transactional(rollbackFor = Exception.class)
        public List<ShiftDrawerCount> initShiftDrawerCount(int propertyId, List<ShiftDrawerCount> shiftDrawerCountList, List<Long> queryTimes) {
            // 查询初始金额配置
            DrawerCountAmountConfig drawerCountAmountConfig = drawerCountAmountConfigService.get(propertyId);
            BigDecimal drawerInit = Objects.isNull(drawerCountAmountConfig) ?
                    DrawerCountCons.DEFAULT_DRAWER_INIT : drawerCountAmountConfig.getDrawerInit();

            // 查询抽屉数量配置
            DrawerCountNumberConfig drawerCountNumberConfig = drawerCountNumberConfigService.get(propertyId);
            int drawerNumber = Objects.isNull(drawerCountNumberConfig) ?
                    DrawerCountCons.DEFAULT_DRAWER_NUMBER : drawerCountNumberConfig.getDrawerNumber();

            // 查询币种配置
            DrawerCountCurrencyConfig drawerCountCurrencyConfig = drawerCountCurrencyConfigService.get(propertyId);
            int drawerCurrency = Objects.isNull(drawerCountCurrencyConfig) ?
                    DrawerCountCons.DEFAULT_DRAWER_CURRENCY : drawerCountCurrencyConfig.getDrawerCurrency();

            Map<Long, List<ShiftDrawerCount>> targetDate2ShiftDrawerCountList = shiftDrawerCountList.stream()
                    .collect(Collectors.groupingBy(ShiftDrawerCount::getTargetDate));

            List<ShiftDrawerCount> insertShiftDrawerCountList = Lists.newArrayList();
            queryTimes.forEach(targetDate -> {
                if (CollectionUtils.isNotEmpty(targetDate2ShiftDrawerCountList.get(targetDate))) {
                    return;
                }
                for (ShiftEnum shift : ShiftEnum.values()) {
                    for (int i = 1; i <= drawerNumber; i++) {
                        insertShiftDrawerCountList.add(new ShiftDrawerCount()
                                .setPropertyId(propertyId)
                                .setTargetDate(targetDate)
                                .setShift(shift.getCode())
                                .setDrawer(i)
                                .setDrawerIn("")
                                .setDrawerOut("")
                                .setDrawerInit(drawerInit)
                                .setDrawerCurrency(drawerCurrency)
                                .setWitnessBy(BaseConstant.ZERO)
                                .setWitnessAuditTime(BaseConstant.LONG_ZERO));
                    }
                }
            });

            // 批量新增
            saveBatch(insertShiftDrawerCountList);
            return insertShiftDrawerCountList;
        }

    }