# TaRecordsStatusController.java

    package cloud.wconnect.finance.controller.ta;

    import cloud.wconnect.commonlib.annotation.ApiId;
    import cloud.wconnect.commonlib.component.SessionContext;
    import cloud.wconnect.commonlib.cons.BaseErrorCodeEnum;
    import cloud.wconnect.commonlib.cons.CommonErrorI18nConstant;
    import cloud.wconnect.commonlib.cons.CommonFieldI18nConstant;
    import cloud.wconnect.commonlib.exception.BadRequestException;
    import cloud.wconnect.commonlib.exception.ForbiddenException;
    import cloud.wconnect.commonlib.exception.InternalServerErrorException;
    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.finance.cons.ta.TaI18nConstant;
    import cloud.wconnect.finance.cons.ta.TaRecordsStatusEnum;
    import cloud.wconnect.finance.entity.ta.TaRecordNotes;
    import cloud.wconnect.finance.entity.ta.TaRecordsStatus;
    import cloud.wconnect.finance.pojo.dto.ta.*;
    import cloud.wconnect.finance.pojo.query.ta.TaCreditCardListQuery;
    import cloud.wconnect.finance.pojo.query.ta.TaRecordsStatusExportQuery;
    import cloud.wconnect.finance.pojo.query.ta.TaRecordsStatusQuery;
    import cloud.wconnect.finance.pojo.query.ta.TaRecordsStatusTotalQuery;
    import cloud.wconnect.finance.service.ta.ITaRecordNotesService;
    import cloud.wconnect.finance.service.ta.ITaRecordsStatusService;
    import com.google.common.collect.ImmutableMap;
    import com.google.common.collect.Lists;
    import jakarta.annotation.Resource;
    import jakarta.servlet.http.HttpServletResponse;
    import jakarta.validation.Valid;
    import org.apache.commons.lang3.StringUtils;
    import org.jxls.transform.poi.JxlsPoiTemplateFillerBuilder;
    import org.springframework.context.i18n.LocaleContextHolder;
    import org.springframework.core.io.ClassPathResource;
    import org.springframework.web.bind.annotation.*;

    import java.io.IOException;
    import java.io.InputStream;
    import java.util.*;
    import java.util.stream.Collectors;

    /**
     * <p>
     * TA Records status 前端控制器
     * </p>
     *
     * @author zc
     * @since 2023-07-07
     */
    @RestController
    @RequestMapping("/ta")
    public class TaRecordsStatusController {
        @Resource
        private ITaRecordsStatusService taRecordsStatusService;
        @Resource
        private ITaRecordNotesService taRecordNotesService;
        @Resource
        private PropertyI18nRedisService propertyI18nRedisService;
        @Resource
        private SessionContext sessionContext;
        @Resource
        private I18nMessageUtils i18nMessageUtils;

        /**
         * 获取 Records Status 数据集合
         *
         * @param property             资产标识
         * @param taRecordsStatusQuery 查询条件
         * @return TaRecordsStatusDTO 集合
         * @author zc
         * @date 2023/7/10
         */
        @ApiId(442)
        @GetMapping
        public ListPagedDTO<TaRecordsStatusDTO> list(@RequestParam String property, @Valid TaRecordsStatusQuery taRecordsStatusQuery) {
            if (Objects.nonNull(taRecordsStatusQuery.getFromDate())
                    && Objects.nonNull(taRecordsStatusQuery.getToDate())) {
                // 校验起止时间参数
                checkDateParam(taRecordsStatusQuery.getFromDate(), taRecordsStatusQuery.getToDate());
            }

            // 校验数据状态参数
            checkStatus(taRecordsStatusQuery.getStatus());

            int tenantId = sessionContext.getTenantSymbolBO().getId();
            int propertyId = sessionContext.getPropertyId(property);

            return taRecordsStatusService.list(tenantId, propertyId, taRecordsStatusQuery);
        }

        /**
         * 获取数据各状态统计
         *
         * @param property   资产标识
         * @param totalQuery 查询条件
         * @return TaRecordsStatusTotalDTO
         * @author zc
         * @date 2023/7/10
         */
        @ApiId(443)
        @GetMapping("/status-total")
        public TaRecordsStatusTotalDTO getTotal(@RequestParam String property, @Valid TaRecordsStatusTotalQuery totalQuery) {
            if (Objects.nonNull(totalQuery.getFromDate())
                    && Objects.nonNull(totalQuery.getToDate())) {
                // 校验起止时间参数
                checkDateParam(totalQuery.getFromDate(), totalQuery.getToDate());
            }
            int propertyId = sessionContext.getPropertyId(property);
            return taRecordsStatusService.getTotal(propertyId, totalQuery);
        }

        /**
         * 获取数据详情
         *
         * @param id       数据 id
         * @param property 资产标识
         * @return TaRecordsStatusDetailDTO
         * @author zc
         * @date 2023/7/11
         */
        @ApiId(444)
        @GetMapping("/{id:\\d+}")
        public TaRecordsStatusDetailDTO getDetail(@PathVariable long id, @RequestParam String property) {
            int tenantId = sessionContext.getTenantSymbolBO().getId();
            int propertyId = sessionContext.getPropertyId(property);

            // 校验数据是否存在
            TaRecordsStatus taRecordsStatus = taRecordsStatusService.checkExist(id);
            // 校验数据归属
            taRecordsStatusService.checkBelong(propertyId, taRecordsStatus);

            return taRecordsStatusService.getDetail(tenantId, taRecordsStatus);
        }

        /**
         * 更新数据状态
         *
         * @param id              数据 id
         * @param property        资产标识
         * @param updateStatusDTO 更新状态数据
         * @return empty map
         * @author zc
         * @date 2023/7/11
         */
        @ApiId(445)
        @PutMapping("/{id:\\d+}/status")
        public Map<Object, Object> updateStatus(@PathVariable long id, @RequestParam String property,
                                                @RequestBody @Valid TaUpdateStatusDTO updateStatusDTO) {
            int propertyId = sessionContext.getPropertyId(property);

            // 校验数据是否存在
            TaRecordsStatus taRecordsStatus = taRecordsStatusService.checkExist(id);
            // 校验数据归属
            taRecordsStatusService.checkBelong(propertyId, taRecordsStatus);

            if (updateStatusDTO.getStatus().equals(TaRecordsStatusEnum.MATCHED.getCode())) {
                updateStatusMatched(taRecordsStatus, updateStatusDTO);
            } else if (updateStatusDTO.getStatus().equals(TaRecordsStatusEnum.NOT_IN_PMS.getCode())) {
                updateStatusNotInPms(taRecordsStatus, updateStatusDTO);
            } else if (updateStatusDTO.getStatus().equals(TaRecordsStatusEnum.TBA.getCode())) {
                updateStatusTba(taRecordsStatus, updateStatusDTO);
            } else if (updateStatusDTO.getStatus().equals(TaRecordsStatusEnum.IGNORE.getCode())) {
                updateStatus(taRecordsStatus, updateStatusDTO);
            } else if (updateStatusDTO.getStatus().equals(TaRecordsStatusEnum.ILWDP.getCode())) {
                updateStatus(taRecordsStatus, updateStatusDTO);
            } else {
                // 只能变更为 (1:Matched; 5:TBA; 6:Ignore; 7:Not In PMS; 8:ILWDP)
                throw new BadRequestException(CommonErrorI18nConstant.MUST_IN_LIST,
                        i18nMessageUtils.getErrorParamMapNeedTranslate(CommonFieldI18nConstant.STATUS,
                                List.of(TaRecordsStatusEnum.MATCHED.getI18nCode(), TaRecordsStatusEnum.TBA.getI18nCode(),
                                        TaRecordsStatusEnum.IGNORE.getI18nCode(), TaRecordsStatusEnum.NOT_IN_PMS.getI18nCode(),
                                        TaRecordsStatusEnum.ILWDP.getI18nCode())),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The status must be in [1:matched; 5:TBA; 6:Ignore; 7:Not In PMS; 8:ILWDP]; " +
                                "updateStatus: " + updateStatusDTO.getStatus());
            }
            return Collections.emptyMap();
        }

        /**
         * 更新数据状态为 Matched
         *
         * @param taRecordsStatus 原数据
         * @param updateStatusDTO 更新状态数据
         * @author zc
         * @date 2023/7/11
         */
        private void updateStatusMatched(TaRecordsStatus taRecordsStatus, TaUpdateStatusDTO updateStatusDTO) {
            if (taRecordsStatus.getStatus().equals(TaRecordsStatusEnum.MISS_PMS.getCode())) {
                // 验证 creditCardIds 和 confirmation 不能同时为空
                if (StringUtils.isBlank(updateStatusDTO.getCreditCardIds())
                        && StringUtils.isBlank(updateStatusDTO.getConfirmation())) {
                    throw new BadRequestException(CommonErrorI18nConstant.CANNOT_EMPTY,
                            i18nMessageUtils.getErrorParamMapNotNeedTranslate("creditCardIds & confirmation"),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            "The creditCardIds and confirmation cannot all be blank; updateStatus: " + updateStatusDTO.getStatus());
                }
                taRecordsStatusService.updateStatusMissPmsToMatched(taRecordsStatus, updateStatusDTO,
                        sessionContext.getLoginUserId());
            } else if (taRecordsStatus.getStatus().equals(TaRecordsStatusEnum.PRICE_VARIANCE.getCode())) {
                taRecordsStatusService.updateStatusPriceVarianceToMatched(taRecordsStatus, updateStatusDTO,
                        sessionContext.getLoginUserId());
            } else {
                throw new BadRequestException(CommonErrorI18nConstant.MUST_IN_LIST,
                        i18nMessageUtils.getErrorParamMapNeedTranslate(CommonFieldI18nConstant.STATUS,
                                List.of(TaRecordsStatusEnum.MISS_PMS.getI18nCode(), TaRecordsStatusEnum.PRICE_VARIANCE.getI18nCode())),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The originStatus must be in [3:missPms; 4:priceVariance]; originStatus: " + taRecordsStatus.getStatus());
            }
        }

        /**
         * 更新数据状态为 Not in PMS
         *
         * @param taRecordsStatus 原数据
         * @param updateStatusDTO 更新状态数据
         * @author zc
         * @date 2023/7/11
         */
        private void updateStatusNotInPms(TaRecordsStatus taRecordsStatus, TaUpdateStatusDTO updateStatusDTO) {
            if (!taRecordsStatus.getStatus().equals(TaRecordsStatusEnum.MISS_PMS.getCode())) {
                throw new BadRequestException(CommonErrorI18nConstant.MUST_IN_LIST,
                        i18nMessageUtils.getErrorParamMapNeedTranslate(CommonFieldI18nConstant.STATUS,
                                List.of(TaRecordsStatusEnum.MISS_PMS.getI18nCode())),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The originStatus must be in [3:missPms]; originStatus: " + taRecordsStatus.getStatus());
            }
            taRecordsStatusService.updateStatus(taRecordsStatus, updateStatusDTO, sessionContext.getLoginUserId());
        }

        /**
         * 更新数据状态为 TBA
         *
         * @param taRecordsStatus 原数据
         * @param updateStatusDTO 更新状态数据
         * @author zc
         * @date 2023/7/11
         */
        private void updateStatusTba(TaRecordsStatus taRecordsStatus, TaUpdateStatusDTO updateStatusDTO) {
            if (!taRecordsStatus.getStatus().equals(TaRecordsStatusEnum.MISS_TA.getCode())) {
                throw new BadRequestException(CommonErrorI18nConstant.MUST_IN_LIST,
                        i18nMessageUtils.getErrorParamMapNeedTranslate(CommonFieldI18nConstant.STATUS,
                                List.of(TaRecordsStatusEnum.MISS_TA.getI18nCode())),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The originStatus must be in [2:missTA]; originStatus: " + taRecordsStatus.getStatus());
            }
            if (StringUtils.isBlank(updateStatusDTO.getAirline())) {
                throw new BadRequestException(CommonErrorI18nConstant.CANNOT_EMPTY,
                        i18nMessageUtils.getErrorParamMapNeedTranslate(TaI18nConstant.AIRLINE),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The airline cannot be blank; updateStatus: " + updateStatusDTO.getStatus());
            }
            taRecordsStatusService.updateStatusTba(taRecordsStatus, updateStatusDTO, sessionContext.getLoginUserId());
        }

        /**
         * 更新数据状态(Ignore、ILWDP)
         *
         * @param taRecordsStatus 原数据
         * @param updateStatusDTO 更新状态数据
         * @author zc
         * @date 2023/7/11
         */
        private void updateStatus(TaRecordsStatus taRecordsStatus, TaUpdateStatusDTO updateStatusDTO) {
            if (!taRecordsStatus.getStatus().equals(TaRecordsStatusEnum.MISS_TA.getCode())) {
                throw new BadRequestException(CommonErrorI18nConstant.MUST_IN_LIST,
                        i18nMessageUtils.getErrorParamMapNeedTranslate(CommonFieldI18nConstant.STATUS,
                                List.of(TaRecordsStatusEnum.MISS_TA.getI18nCode())),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The originStatus must be in [2:missTA]; originStatus: " + taRecordsStatus.getStatus());
            }
            taRecordsStatusService.updateStatus(taRecordsStatus, updateStatusDTO, sessionContext.getLoginUserId());
        }

        /**
         * 回滚数据状态
         *
         * @param id       数据 id
         * @param property 资产标识
         * @return empty map
         * @author zc
         * @date 2023/7/12
         */
        @ApiId(446)
        @PutMapping("/{id:\\d+}/undo")
        public Map<Object, Object> undo(@PathVariable long id, @RequestParam String property) {
            int propertyId = sessionContext.getPropertyId(property);

            // 校验数据是否存在
            TaRecordsStatus taRecordsStatus = taRecordsStatusService.checkExist(id);
            // 校验数据归属
            taRecordsStatusService.checkBelong(propertyId, taRecordsStatus);

            // 校验目标数据是否支持 undo
            if (StringUtils.isEmpty(taRecordsStatus.getStatusDetail())) {
                throw new BadRequestException(CommonErrorI18nConstant.CANNOT_EMPTY,
                        i18nMessageUtils.getErrorParamMapNotNeedTranslate("statusDetail"),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The taRecordsStatus statusDetail is empty, cannot undo; id:" + id);
            }
            if (taRecordsStatus.getStatus().equals(TaRecordsStatusEnum.MATCHED.getCode())) {
                taRecordsStatusService.undoMatched(taRecordsStatus);
            } else if (taRecordsStatus.getStatus().equals(TaRecordsStatusEnum.NOT_IN_PMS.getCode())) {
                taRecordsStatusService.undoNotInPms(taRecordsStatus);
            } else if (taRecordsStatus.getStatus().equals(TaRecordsStatusEnum.TBA.getCode())) {
                taRecordsStatusService.undoTba(taRecordsStatus);
            } else if (taRecordsStatus.getStatus().equals(TaRecordsStatusEnum.IGNORE.getCode())) {
                taRecordsStatusService.undo(taRecordsStatus);
            } else if (taRecordsStatus.getStatus().equals(TaRecordsStatusEnum.ILWDP.getCode())) {
                taRecordsStatusService.undo(taRecordsStatus);
            }

            return Collections.emptyMap();
        }

        /**
         * 导出数据为 Excel
         *
         * @param property    资产标识
         * @param exportQuery 查询条件
         * @param response    response
         * @author zc
         * @date 2023/7/12
         */
        @ApiId(447)
        @GetMapping("/report")
        public void exportFile(@RequestParam String property, @Valid TaRecordsStatusExportQuery exportQuery,
                               HttpServletResponse response) {
            int propertyId = sessionContext.getPropertyId(property);
            PropertyDTO propertyDTO = propertyI18nRedisService.get(propertyId);

            String tmplName;
            HashMap<String, Object> data;
            List<Integer> queryStatus = exportQuery.getStatus();
            if (queryStatus.size() == 1) {
                int status = queryStatus.get(0);
                if (status == TaRecordsStatusEnum.MATCHED.getCode()
                        || status == TaRecordsStatusEnum.TBA.getCode()
                        || status == TaRecordsStatusEnum.ILWDP.getCode()) {
                    tmplName = "ta_comparative_data_tmpl";
                    data = taRecordsStatusService.exportComparativeData(propertyDTO, exportQuery);
                } else if (status == TaRecordsStatusEnum.PRICE_VARIANCE.getCode()) {
                    tmplName = "ta_comparative_data_price_variance_tmpl";
                    data = taRecordsStatusService.exportComparativeData(propertyDTO, exportQuery);
                } else if (status == TaRecordsStatusEnum.MISS_TA.getCode()
                        || status == TaRecordsStatusEnum.IGNORE.getCode()) {
                    tmplName = "ta_pms_data_tmpl";
                    data = taRecordsStatusService.exportCreditCardData(propertyDTO, exportQuery);
                } else if (status == TaRecordsStatusEnum.MISS_PMS.getCode()
                        || status == TaRecordsStatusEnum.NOT_IN_PMS.getCode()) {
                    tmplName = "ta_portal_data_tmpl";
                    data = taRecordsStatusService.exportPortalData(propertyDTO, exportQuery);
                } else {
                    throw new BadRequestException(CommonErrorI18nConstant.MUST_IN_LIST,
                            i18nMessageUtils.getErrorParamMapNeedTranslate(CommonFieldI18nConstant.STATUS,
                                    List.of(TaRecordsStatusEnum.MATCHED.getI18nCode(), TaRecordsStatusEnum.MISS_TA.getI18nCode(),
                                            TaRecordsStatusEnum.MISS_PMS.getI18nCode(), TaRecordsStatusEnum.PRICE_VARIANCE.getI18nCode(),
                                            TaRecordsStatusEnum.TBA.getI18nCode(), TaRecordsStatusEnum.IGNORE.getI18nCode(),
                                            TaRecordsStatusEnum.NOT_IN_PMS.getI18nCode(), TaRecordsStatusEnum.ILWDP.getI18nCode())),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            "The queryStatus must be in [1:matched; 2:missTA; 3:missPms; 4:priceVariance; 5:TBA;" +
                                    " 6:Ignore; 7:Not in PMS; 8:ILWDP]; queryStatus:" + queryStatus);
                }
            } else if (queryStatus.size() == 2) {
                if (queryStatus.containsAll(Lists.newArrayList(TaRecordsStatusEnum.MATCHED.getCode(),
                        TaRecordsStatusEnum.TBA.getCode()))) {
                    tmplName = "ta_comparative_data_tmpl";
                    data = taRecordsStatusService.exportComparativeData(propertyDTO, exportQuery);
                } else {
                    throw new BadRequestException(CommonErrorI18nConstant.MUST_IN_LIST,
                            i18nMessageUtils.getErrorParamMapNeedTranslate(CommonFieldI18nConstant.STATUS,
                                    List.of(TaRecordsStatusEnum.MATCHED.getI18nCode(), TaRecordsStatusEnum.TBA.getI18nCode())),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            "The queryStatus must be in [1,5:matched and TBA]; queryStatus:" + queryStatus);
                }
            } else {
                throw new BadRequestException(CommonErrorI18nConstant.MUST_IN_LIST,
                        i18nMessageUtils.getErrorParamMapNeedTranslate(CommonFieldI18nConstant.STATUS,
                                List.of(TaRecordsStatusEnum.MATCHED.getI18nCode(), TaRecordsStatusEnum.MISS_TA.getI18nCode(),
                                        TaRecordsStatusEnum.MISS_PMS.getI18nCode(), TaRecordsStatusEnum.PRICE_VARIANCE.getI18nCode(),
                                        TaRecordsStatusEnum.TBA.getI18nCode(), TaRecordsStatusEnum.IGNORE.getI18nCode(),
                                        TaRecordsStatusEnum.NOT_IN_PMS.getI18nCode(), TaRecordsStatusEnum.ILWDP.getI18nCode(),
                                        TaI18nConstant.STATUS_MATCHED_TBA)),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The queryStatus must be in [1:matched; 2:missTA; 3:missPms; 4:priceVariance; 5:TBA;" +
                                " 6:Ignore; 7:Not in PMS; 8:ILWDP; 1,5:matched and TBA]; queryStatus:" + queryStatus);
            }

            try (InputStream inputStream = new ClassPathResource("tmpl-xls/"
                    + tmplName + "_" + LocaleContextHolder.getLocale() + ".xlsx").getInputStream()) {
                data.put("propertyName", propertyDTO.getName());
                data.put("status", StringUtils.join(exportQuery.getStatus().stream()
                        .map(item -> i18nMessageUtils.message(TaRecordsStatusEnum.getI18nCode(item),
                                LocaleContextHolder.getLocale())).collect(Collectors.toList()), ", "));

                String fileName = "TA Connect Commission.xlsx";
                response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
                response.addHeader("Access-Control-Expose-Headers", "Content-Disposition");
                response.setHeader("Content-Disposition", String.join("", "attachment; filename=", fileName));

                JxlsPoiTemplateFillerBuilder.newInstance()
                        .withTemplate(inputStream)
                        .buildAndFill(data, response::getOutputStream);
            } catch (IOException e) {
                throw new InternalServerErrorException(BaseErrorCodeEnum.SYSTEM_ERROR.getI18nCode(),
                        BaseErrorCodeEnum.SYSTEM_ERROR.getCode(),
                        "Export file failed; exception:" + e);
            }
        }

        /**
         * 获取 Credit Card 列表
         *
         * @param property       资产标识
         * @param taPmsListQuery 查询条件
         * @return TaPmsListDTO 集合
         * @author zc
         * @date 2023/7/15
         */
        @ApiId(451)
        @GetMapping("/credit-card")
        public ListPagedDTO<TaCreditCardListDTO> listCreditCard(@RequestParam String property, @Valid TaCreditCardListQuery taPmsListQuery) {
            int propertyId = sessionContext.getPropertyId(property);

            return taRecordsStatusService.listCreditCard(propertyId, taPmsListQuery);
        }

        /**
         * 获取备注集合
         *
         * @param id       records id
         * @param property 资产标识
         * @return TaRecordsNoteDTO 集合
         * @author zc
         * @date 2023/7/15
         */
        @ApiId(452)
        @GetMapping("/{id:\\d+}/note")
        public List<TaRecordsNoteDTO> listNotes(@PathVariable long id, @RequestParam String property) {
            int tenantId = sessionContext.getTenantSymbolBO().getId();
            int propertyId = sessionContext.getPropertyId(property);

            // 校验数据是否存在
            TaRecordsStatus taRecordsStatus = taRecordsStatusService.checkExist(id);
            // 校验数据归属
            taRecordsStatusService.checkBelong(propertyId, taRecordsStatus);

            return taRecordNotesService.list(tenantId, propertyId, id);
        }

        /**
         * 新增备注
         *
         * @param id               records id
         * @param property         资产标识
         * @param taRecordsNoteDTO 备注内容
         * @return 备注数据 id
         * @author zc
         * @date 2023/7/18
         */
        @ApiId(453)
        @PostMapping("/{id:\\d+}/note")
        public Map<String, Long> addNote(@PathVariable long id, @RequestParam String property,
                                         @Valid @RequestBody TaRecordsNoteDTO taRecordsNoteDTO) {
            int propertyId = sessionContext.getPropertyId(property);

            // 校验数据是否存在
            TaRecordsStatus taRecordsStatus = taRecordsStatusService.checkExist(id);
            // 校验数据归属
            taRecordsStatusService.checkBelong(propertyId, taRecordsStatus);

            TaRecordNotes taRecordNotes = new TaRecordNotes()
                    .setPropertyId(propertyId)
                    .setRId(id)
                    .setNote(taRecordsNoteDTO.getNote())
                    .setOperatorId(sessionContext.getLoginUserId())
                    .setCreateTime(DateTimeUtils.nowTimestamp());

            taRecordNotesService.save(taRecordNotes);

            return ImmutableMap.of("id", taRecordNotes.getId());
        }

        /**
         * 删除备注
         *
         * @param id       records id
         * @param noteId   备注 id
         * @param property 资产标识
         * @return empty map
         * @author zc
         * @date 2023/7/18
         */
        @ApiId(454)
        @DeleteMapping("/{id:\\d+}/note/{noteId:\\d+}")
        public Map<Object, Object> removeNote(@PathVariable long id, @PathVariable long noteId,
                                              @RequestParam String property) {
            int propertyId = sessionContext.getPropertyId(property);

            // 校验数据是否存在
            TaRecordsStatus taRecordsStatus = taRecordsStatusService.checkExist(id);
            // 校验数据归属
            taRecordsStatusService.checkBelong(propertyId, taRecordsStatus);

            // 校验备注数据是否存在
            TaRecordNotes taRecordNotes = taRecordNotesService.checkExist(noteId);
            // 校验备注数据归属
            taRecordNotesService.checkBelong(propertyId, id, taRecordNotes);
            // 校验当前用户是否是创建人
            if (!taRecordNotes.getOperatorId().equals(sessionContext.getLoginUserId())) {
                throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                        BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(),
                        "The login user is not creator; loginUserId:" + sessionContext.getLoginUserId()
                                + ", operatorId:" + taRecordNotes.getOperatorId());
            }

            taRecordNotesService.removeById(noteId);

            return Collections.emptyMap();
        }

        /**
         * 校验起止时间参数
         *
         * @param fromDate 开始时间
         * @param toDate   结束时间
         * @author zc
         * @date 2023/7/10
         */
        private void checkDateParam(long fromDate, long toDate) {
            // 开始时间不能大于结束时间
            if (fromDate > toDate) {
                throw new BadRequestException(CommonErrorI18nConstant.START_DATE_CANNOT_GREATER_THAN_END_DATE,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The fromDate cannot be greater than the toDate");
            }
        }

        /**
         * 校验数据状态参数
         *
         * @param status 状态集合
         * @author zc
         * @date 2023/7/10
         */
        private void checkStatus(List<Integer> status) {
            if (!Lists.newArrayList(TaRecordsStatusEnum.MATCHED.getCode(), TaRecordsStatusEnum.MISS_TA.getCode(),
                    TaRecordsStatusEnum.MISS_PMS.getCode(), TaRecordsStatusEnum.PRICE_VARIANCE.getCode(),
                    TaRecordsStatusEnum.TBA.getCode(), TaRecordsStatusEnum.IGNORE.getCode(),
                    TaRecordsStatusEnum.NOT_IN_PMS.getCode(), TaRecordsStatusEnum.ILWDP.getCode()).containsAll(status)) {
                throw new BadRequestException(CommonErrorI18nConstant.MUST_IN_LIST,
                        i18nMessageUtils.getErrorParamMapNeedTranslate(CommonFieldI18nConstant.STATUS,
                                List.of(TaRecordsStatusEnum.MATCHED.getI18nCode(), TaRecordsStatusEnum.MISS_TA.getI18nCode(),
                                        TaRecordsStatusEnum.MISS_PMS.getI18nCode(), TaRecordsStatusEnum.PRICE_VARIANCE.getI18nCode(),
                                        TaRecordsStatusEnum.TBA.getI18nCode(), TaRecordsStatusEnum.IGNORE.getI18nCode(),
                                        TaRecordsStatusEnum.NOT_IN_PMS.getI18nCode(), TaRecordsStatusEnum.ILWDP.getI18nCode())),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The status must be in [1:matched; 2:missTA; 3:missPms; 4:priceVariance; 5:TBA; 6:Ignore;" +
                                " 7:Not in PMS; 8:ILWDP]; status param is: " + status);
            }
        }
    }