# OverBalanceRvController.java

    package cloud.wconnect.frontoffice.controller.guestledgercheck;

    import cloud.wconnect.commonlib.annotation.ApiId;
    import cloud.wconnect.commonlib.component.BeanMapper;
    import cloud.wconnect.commonlib.component.SessionContext;
    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.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.commonlib.util.I18nMessageUtils;
    import cloud.wconnect.frontoffice.entity.guestledgercheck.GstLedgOpBalanceReservation;
    import cloud.wconnect.frontoffice.entity.guestledgercheck.GstLedgOvBalanceReservation;
    import cloud.wconnect.frontoffice.pojo.dto.guestledgercheck.OpBalanceRvDTO;
    import cloud.wconnect.frontoffice.pojo.dto.guestledgercheck.OvBalanceRvAmountDTO;
    import cloud.wconnect.frontoffice.pojo.dto.guestledgercheck.OvBalanceRvDTO;
    import cloud.wconnect.frontoffice.service.guestledgercheck.IGstLedgOvBalanceReservationService;
    import cloud.wconnect.frontoffice.service.guestledgercheck.IGstLedgOverBalanceConfigService;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.*;

    import jakarta.validation.Valid;

    import java.text.MessageFormat;
    import java.util.*;

    /**
     * @author lichen
     * @description 控制器类: in-house Over Balance Reservation 相关的业务逻辑
     * @date 2023/3/18 13:02
     */
    @RestController
    @RequestMapping("/guest-ledger-check/ov-balance")
    public class OverBalanceRvController {

        @Autowired
        private SessionContext sessionContext;
        @Autowired
        private IGstLedgOvBalanceReservationService ovBalanceReservationService;
        @Autowired
        private IGstLedgOverBalanceConfigService overBalanceConfigService;
        @Autowired
        private BeanMapper beanMapper;
        @Autowired
        private PropertyI18nRedisService propertyI18nRedisService;
        @Autowired
        private I18nMessageUtils i18nMessageUtils;

        /**
         * 查询over balance Reservation列表
         *
         * @param property 资产标识
         * @param page     页码
         * @param size     每页大小
         * @return cloud.wconnect.commonlib.pojo.dto.ListPagedDTO<cloud.wconnect.frontoffice.pojo.dto.guestledgercheck.OvBalanceRvDTO>
         * @author lichen
         * @date 2025/8/25
         */
        @ApiId(124)
        @GetMapping
        public ListPagedDTO<OvBalanceRvDTO> page(String property, @RequestParam(name = "page") Integer page,
                                                 @RequestParam(name = "size") Integer size) {
            int propertyId = sessionContext.getPropertyId(property);
            Integer tenantId = sessionContext.getTenantSymbolBO().getId();
            return ovBalanceReservationService.pageList(tenantId, propertyId, page, size);
        }

        /**
         * 新增或编辑over balance Reservation信息
         *
         * @param rvId     Reservation ID
         * @param property 资产标识
         * @param rvDto    Reservation信息
         * @return java.util.Map<java.lang.String, java.lang.Long>
         * @author lichen
         * @date 2025/8/25
         */
        @ApiId(125)
        @PostMapping("/{rvId:\\d+}")
        public Map<String, Long> save(@PathVariable long rvId, String property,
                                      @Valid @RequestBody OvBalanceRvDTO rvDto) {
            int propertyId = sessionContext.getPropertyId(property);
            int loginUserId = sessionContext.getLoginUserId();
            //请求参数 校验处理
            String crsNumberListStr = StringUtils.join(rvDto.getCrsNumberList(), ",");
            if (crsNumberListStr.length() > 200) {
                throw new BadRequestException(CommonErrorI18nConstant.OVERLENGTH,
                        i18nMessageUtils.getErrorParamMapNotNeedTranslate("crsNumberListStr", 200),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "The CRS Number List is limited 200 characters");
            }
            // 获取余额下限配置，校验当前余额是否大于等于余额下限
            overBalanceConfigService.checkOverBalance(propertyId, rvDto.getCurrentBalance());

            //OvBalanceRvDTO -> GstLedgOvBalanceReservation
            GstLedgOvBalanceReservation newRv = beanMapper.map(rvDto, GstLedgOvBalanceReservation.class, rv -> {
                rv.setCrsNumbers(crsNumberListStr);
                rv.setPropertyId(propertyId);
            });
            if (rvId == 0) {
                //新增Reservation
                newRv.setCreatedBy(loginUserId);
                newRv.setCreatedTime(DateTimeUtils.nowTimestamp());
                newRv.setCurrBalUpdatedBy(loginUserId);
                newRv.setCurrBalUpdatedTime(DateTimeUtils.nowTimestamp());
                newRv.setAuthAmntUpdateBy(loginUserId);
                newRv.setAuthAmntUpdatedTime(DateTimeUtils.nowTimestamp());
                newRv.setId(null);
                ovBalanceReservationService.save(newRv);
                return Collections.singletonMap("id", newRv.getId());
            }

            //校验Reservation是否存在；校验Reservation所属资产
            GstLedgOvBalanceReservation originRv = checkExistAndBelongProperty(rvId, propertyId);

            newRv.setId(originRv.getId());
            newRv.setLastUpdatedBy(loginUserId);
            newRv.setLastUpdatedTime(DateTimeUtils.nowTimestamp());
            //如果 Current Balance 值发生变化，记录更新人和更新时间
            if (newRv.getCurrentBalance().compareTo(originRv.getCurrentBalance()) != BaseConstant.ZERO) {
                newRv.setCurrBalUpdatedBy(loginUserId);
                newRv.setCurrBalUpdatedTime(DateTimeUtils.nowTimestamp());
            }
            //如果 Write off Amount 值发生变化，记录更新人和更新时间
            if (newRv.getAuthAmount().compareTo(originRv.getAuthAmount()) != 0) {
                newRv.setAuthAmntUpdateBy(loginUserId);
                newRv.setAuthAmntUpdatedTime(DateTimeUtils.nowTimestamp());
            }
            //do更新
            ovBalanceReservationService.updateById(newRv);
            return Collections.singletonMap("id", rvId);
        }

        /**
         * 移动over balance Reservation信息
         *
         * @param rvId     over balance Reservation ID
         * @param property 资产标识
         * @param rvDto    over balance Reservation 信息
         * @return java.util.Map
         * @author lichen
         * @date 2025/8/25
         */
        @ApiId(126)
        @PostMapping("/{rvId:\\d+}/move")
        public Map move(@PathVariable long rvId, String property,
                        @Valid @RequestBody OpBalanceRvDTO rvDto) {
            int propertyId = sessionContext.getPropertyId(property);
            PropertyDTO propertyDTO = propertyI18nRedisService.get(propertyId);
            int loginUserId = sessionContext.getLoginUserId();

            checkExistAndBelongProperty(rvId, propertyId);

            //校验CRS Number List
            String crsNumberListStr = StringUtils.join(rvDto.getCrsNumberList(), ",");
            if (crsNumberListStr.length() > 200) {
                throw new BadRequestException(CommonErrorI18nConstant.OVERLENGTH,
                        i18nMessageUtils.getErrorParamMapNotNeedTranslate("crsNumberListStr", 200),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "The CRS Number List is limited 200 characters");
            }

            //OpBalanceRvDTO -> GstLedgOpBalanceReservation
            GstLedgOpBalanceReservation newRv = beanMapper.map(rvDto, GstLedgOpBalanceReservation.class, rv -> {
                rv.setCrsNumbers(crsNumberListStr);
                rv.setPropertyId(propertyId);
                rv.setId(null);
            });
            //保存编辑后的Open Balance Reservation，删除迁移的Over Balance Reservation
            ovBalanceReservationService.move(propertyDTO, rvId, newRv, loginUserId);
            return Collections.emptyMap();
        }

        /**
         * 删除over balance Reservation信息
         *
         * @param rvId     over balance Reservation ID
         * @param property 资产标识
         * @return java.lang.Boolean
         * @author lichen
         * @date 2025/8/25
         */
        @ApiId(127)
        @DeleteMapping("/{rvId:\\d+}")
        public Boolean remove(@PathVariable long rvId, String property) {
            int propertyId = sessionContext.getPropertyId(property);
            checkExistAndBelongProperty(rvId, propertyId);
            return ovBalanceReservationService.removeById(rvId);
        }

        /**
         * 编辑over balance Reservation中的Current Amount/Auth Amount
         *
         * @param rvId                 over balance Reservation ID
         * @param property             资产标识
         * @param ovBalanceRvAmountDTO 编辑信息
         * @return java.util.Map
         * @author lichen
         * @date 2025/8/25
         */
        @ApiId(128)
        @PutMapping("/{rvId:\\d+}/amount")
        public Map updateAmount(@PathVariable long rvId, String property,
                                @Valid @RequestBody OvBalanceRvAmountDTO ovBalanceRvAmountDTO) {
            int propertyId = sessionContext.getPropertyId(property);
            int loginUserId = sessionContext.getLoginUserId();

            if (ovBalanceRvAmountDTO.getType() == OvBalanceRvAmountDTO.TYPE_CURRENT_BALANCE) {
                // 获取余额下限配置，校验当前余额是否大于等于余额下限
                overBalanceConfigService.checkOverBalance(propertyId, ovBalanceRvAmountDTO.getAmount());
            }

            GstLedgOvBalanceReservation oriRv = checkExistAndBelongProperty(rvId, propertyId);

            //更新Current Balance/Auth Amount
            ovBalanceRvAmountDTO.setPropertyId(propertyId);
            ovBalanceRvAmountDTO.setRvId(rvId);
            ovBalanceReservationService.update(ovBalanceRvAmountDTO, oriRv, loginUserId);
            return Collections.emptyMap();
        }

        /**
         * 校验数据是否存在；校验是否归属于当前资产
         *
         * @param ovId       主键id
         * @param propertyId 资产id
         * @return cloud.wconnect.frontoffice.entity.guestledgercheck.GstLedgOvBalanceReservation
         * @author lichen
         * @date 2025/8/25
         */
        private GstLedgOvBalanceReservation checkExistAndBelongProperty(Long ovId, Integer propertyId) {
            GstLedgOvBalanceReservation ovBalanceReservation = ovBalanceReservationService.getById(ovId);
            if (Objects.isNull(ovBalanceReservation)) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_NOT_FOUND,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "id=" + ovId + " ,reservations with over balance not found");
            }
            if (ovBalanceReservation.getPropertyId().compareTo(propertyId) != 0) {
                throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                        BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(),
                        MessageFormat.format("OvBalanceReservation cannot belong property, id:{0}, belongPropertyId:{1}, currentPropertyId:{2}",
                                ovId, ovBalanceReservation.getPropertyId(), propertyId));
            }
            return ovBalanceReservation;
        }

    }