# BadGuestController.java

    package cloud.wconnect.frontoffice.controller.badguest;

    import cloud.wconnect.commonlib.annotation.ApiId;
    import cloud.wconnect.commonlib.component.BeanMapper;
    import cloud.wconnect.commonlib.component.SessionContext;
    import cloud.wconnect.commonlib.cons.BaseErrorCodeEnum;
    import cloud.wconnect.commonlib.cons.CommonErrorI18nConstant;
    import cloud.wconnect.commonlib.cons.SystemPlatformEnum;
    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.pojo.dto.UserDTO;
    import cloud.wconnect.commonlib.redis.PropertyI18nRedisService;
    import cloud.wconnect.commonlib.service.PropertyInternalService;
    import cloud.wconnect.commonlib.service.UserInternalService;
    import cloud.wconnect.commonlib.util.DateTimeUtils;
    import cloud.wconnect.commonlib.util.I18nMessageUtils;
    import cloud.wconnect.frontoffice.cons.badguest.*;
    import cloud.wconnect.frontoffice.entity.badguest.*;
    import cloud.wconnect.frontoffice.parsereport.badguest.IParseFileService;
    import cloud.wconnect.frontoffice.pojo.dto.badguest.*;
    import cloud.wconnect.frontoffice.pojo.query.badguest.BadGuestQuery;
    import cloud.wconnect.frontoffice.pojo.query.badguest.ExportBadGuestQuery;
    import cloud.wconnect.frontoffice.pojo.vo.badguest.*;
    import cloud.wconnect.frontoffice.service.badguest.*;
    import com.google.common.collect.ImmutableMap;
    import com.google.common.collect.Lists;
    import com.google.common.collect.Sets;
    import jakarta.servlet.http.HttpServletResponse;
    import jakarta.validation.Valid;
    import org.apache.commons.collections.CollectionUtils;
    import org.apache.commons.lang3.StringUtils;
    import org.jxls.transform.poi.JxlsPoiTemplateFillerBuilder;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.i18n.LocaleContextHolder;
    import org.springframework.core.io.ClassPathResource;
    import org.springframework.validation.annotation.Validated;
    import org.springframework.web.bind.annotation.*;

    import java.io.IOException;
    import java.io.InputStream;
    import java.text.MessageFormat;
    import java.util.*;
    import java.util.function.Function;
    import java.util.stream.Collectors;

    import static java.util.stream.Collectors.toMap;
    import static java.util.stream.Collectors.toSet;

    /**
     * @author lichen
     * @description 黑名单 控制类
     * @date 2023/3/21 18:20
     */
    @RestController
    @RequestMapping("/guest-blacklist")
    public class BadGuestController {

        @Autowired
        private IBadGuestService badGuestService;
        @Autowired
        private IBadGuestCompanionService badGuestCompanionService;
        @Autowired
        private BeanMapper beanMapper;
        @Autowired
        private PropertyInternalService propertyInternalService;
        @Autowired
        private IBadGuestApproveLogService badGuestApproveLogService;
        @Autowired
        private SessionContext sessionContext;
        @Autowired
        private UserInternalService userInternalService;
        @Autowired
        private IBgReportLogService bgReportLogService;
        @Autowired
        private PropertyI18nRedisService propertyI18nRedisService;
        @Autowired
        private IBgOperateLogService operateLogService;
        @Value("${file-max-size.import-report.blacklist}")
        private long importReportFileMaxSize;
        @Autowired
        private IBadGuestApproveConfigService badGuestApproveConfigService;
        @Autowired
        private IBadGuestApproveLevelService badGuestApproveLevelService;
        @Autowired
        private IBgCommentService commentService;
        @Autowired
        private ApplicationContext applicationContext;
        @Autowired
        private I18nMessageUtils i18nMessageUtils;

        /**
         * 分页查询黑名单信息
         *
         * @param tenant        租户标识
         * @param badGuestQuery 黑名单查询条件
         * @return cloud.wconnect.commonlib.pojo.dto.ListPagedDTO<cloud.wconnect.frontoffice.pojo.vo.badguest.BadGuestVO>
         * @author lichen
         * @date 2024/2/23
         */
        @ApiId(149)
        @GetMapping
        public ListPagedDTO<BadGuestVO> page(String tenant, @Valid BadGuestQuery badGuestQuery) {
            Integer tenantId = sessionContext.getTenantSymbolBO().getId();

            //租户下的所有资产
            List<PropertyDTO> propertyDTOList = propertyInternalService.getPropertyByTenant(tenant);
            List<Integer> propertyIdsByTenant = propertyDTOList.stream().map(PropertyDTO::getId).toList();

            //查询的资产ID为空时，查询租户下所有资产的数据
            if (CollectionUtils.isEmpty(badGuestQuery.getQueryPropertyIds())) {
                badGuestQuery.setQueryPropertyIds(propertyIdsByTenant);
            } else {
                List<Integer> queryPropertyIds = badGuestQuery.getQueryPropertyIds();
                if (queryPropertyIds.size() != Sets.newHashSet(queryPropertyIds).size()) {
                    throw new BadRequestException(CommonErrorI18nConstant.DATA_CANNOT_REPEAT,
                            i18nMessageUtils.getErrorParamMapNotNeedTranslate("propertyList"),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            "queryPropertyIds cannot repeat");
                }
                if (queryPropertyIds.stream().anyMatch(propertyId -> !propertyIdsByTenant.contains(propertyId))) {
                    throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                            BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(),
                            "The selected propertyId not belong current tenant");
                }
            }
            //校验排序字段
            if (StringUtils.isNotBlank(badGuestQuery.getOrderBy()) && !OrderByFieldEnum.isExist(badGuestQuery.getOrderBy())) {
                throw new BadRequestException(CommonErrorI18nConstant.MUST_IN_LIST,
                        i18nMessageUtils.getErrorParamMapNotNeedTranslate("orderBy",
                                Arrays.stream(OrderByFieldEnum.values()).map(OrderByFieldEnum::getField).toList()),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The sort field does not exist");
            }

            return badGuestService.pageQuery(tenantId, propertyDTOList, badGuestQuery);
        }

        /**
         * 查询黑名单详情
         *
         * @param id 黑名单id
         * @return cloud.wconnect.frontoffice.pojo.vo.badguest.BadGuestVO
         * @author lichen
         * @date 2024/2/23
         */
        @ApiId(157)
        @GetMapping("/{id:\\d+}")
        public BadGuestVO getDetail(@PathVariable("id") long id) {
            Integer tenantId = sessionContext.getTenantSymbolBO().getId();
            //校验黑名单数据是否存在
            BadGuest badGuest = badGuestService.checkExist(id);
            //查询黑名单详情
            return badGuestService.getDetail(tenantId, badGuest);
        }

        /**
         * @param id 黑名单主键id
         * @return java.util.List<cloud.wconnect.frontoffice.pojo.view.badguest.BgApproveLevelVO>
         * @description 查询黑名单审批记录
         * @author lichen
         * @date 2023/4/6 15:02
         */
        @ApiId(285)
        @GetMapping("/approveLog/{id:\\d+}")
        public List<BgApproveLevelVO> listApproveLog(@PathVariable("id") long id) {
            //校验黑名单数据是否存在
            BadGuest badGuest = badGuestService.checkExist(id);
            //获取资产
            PropertyDTO propertyDTO = propertyI18nRedisService.get(badGuest.getPropertyId());
            //查询黑名单关联的审核日志集合
            return badGuestApproveLogService.listApproveRecord(badGuest, propertyDTO);
        }

        /**
         * 保存黑名单
         *
         * @param id          主键id
         * @param property    资产标识
         * @param badGuestDTO 黑名单数据
         * @return java.util.Map
         * @author lichen
         * @date 2025/8/26
         */
        @ApiId(158)
        @PostMapping("/{id:\\d+}")
        public Map save(@PathVariable("id") long id, String property,
                        @Validated @RequestBody BadGuestDTO badGuestDTO) {
            if (badGuestDTO.getCheckInDate() > badGuestDTO.getCheckOutDate()) {
                throw new BadRequestException(CommonErrorI18nConstant.CHECK_IN_DATE_CANNOT_GREATER_THAN_CHECK_OUT_DATE,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "CheckOutDate must be greater than CheckInDate");
            }

            if (BlacklistTypeEnum.GUEST_ROOMS.getType() == badGuestDTO.getType()) {
                if (Objects.isNull(badGuestDTO.getLastCheckInDate()) || Objects.isNull(badGuestDTO.getLastCheckOutDate())) {
                    throw new BadRequestException(CommonErrorI18nConstant.CANNOT_EMPTY,
                            i18nMessageUtils.getErrorParamMapNeedTranslate(BlacklistFieldI18nConstant.LAST_CHECK_IN_DATE_AND_CHECK_OUT_DATE),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            "Last stay at our hotel cannot be null");
                }
                if (badGuestDTO.getLastCheckInDate() > badGuestDTO.getLastCheckOutDate()) {
                    throw new BadRequestException(CommonErrorI18nConstant.CHECK_IN_DATE_CANNOT_GREATER_THAN_CHECK_OUT_DATE,
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            "LastCheckOutDate must be greater than LastCheckInDate");
                }
            } else {
                badGuestDTO.setLastCheckInDate(0L);
                badGuestDTO.setLastCheckOutDate(0L);
            }

            //校验有且只有一个主要客人
            List<CompanionDTO> isPrimaryList = badGuestDTO.getCompanions().stream().filter(CompanionDTO::getIsPrimary).toList();
            if (isPrimaryList.size() != 1) {
                throw new BadRequestException(CommonErrorI18nConstant.LIST_SIZE_MUST_BE,
                        i18nMessageUtils.getErrorParamMapNotNeedTranslate("isPrimaryList", 1),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "There is one and only one main guest");
            }
            badGuestDTO.setFirstName(isPrimaryList.get(0).getFirstName());
            badGuestDTO.setLastName(isPrimaryList.get(0).getLastName());

            int propertyId = sessionContext.getPropertyId(property);
            PropertyDTO propertyDTO = propertyI18nRedisService.get(propertyId);
            int loginUserId = sessionContext.getLoginUserId();

            if (id > 0) {
                //校验黑名单是否存在
                BadGuest oriBadGuest = badGuestService.checkExist(id);
                //校验黑名单是否归属于当前资产
                badGuestService.checkBelong(oriBadGuest, propertyId);
                if (oriBadGuest.getApproveStatus() != BlacklistApproveStatusEnum.PENDING.getStatus()) {
                    throw new BadRequestException(CommonErrorI18nConstant.SPECIFIC_STATE_CAN_OPERATE,
                            i18nMessageUtils.getErrorParamMapNeedTranslate(BlacklistApproveStatusEnum.PENDING.getI18nCode(), BlacklistFieldI18nConstant.OPERATE_EDIT),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            "Only Pending can be modified");
                }
                //只有创建人可以修改数据
                checkCurrentUserIsHavePermission(oriBadGuest, loginUserId);
                //修改
                badGuestDTO.setId(id);
                badGuestDTO.setPropertyId(propertyId);
                badGuestDTO.setLastUpdatedBy(loginUserId);
                badGuestDTO.setLastUpdatedTime(DateTimeUtils.nowTimestamp());
                badGuestService.update(oriBadGuest, badGuestDTO, propertyDTO, loginUserId);

                return ImmutableMap.of("id", id);
            } else {
                //新增
                badGuestDTO.setId(null);
                badGuestDTO.setPropertyId(propertyId);
                return ImmutableMap.of("id", badGuestService.save(badGuestDTO, propertyDTO, loginUserId));
            }
        }

        /**
         * 撤回审核中的数据
         *
         * @param id       数据id
         * @param property 资产标识
         * @param opinion  资产标识
         * @return java.util.Map
         * @author lichen
         * @date 2025/8/26
         */
        @ApiId(159)
        @PutMapping("/withdrawn/{id:\\d+}")
        public Map withdrawn(@PathVariable("id") long id, String property,
                             @RequestParam(name = "opinion", required = false) String opinion) {
            int propertyId = sessionContext.getPropertyId(property);
            PropertyDTO propertyDTO = propertyI18nRedisService.get(propertyId);
            int loginUserId = sessionContext.getLoginUserId();

            //校验黑名单是否存在
            BadGuest oriBadGuest = badGuestService.checkExist(id);
            //校验黑名单是否归属于当前资产
            badGuestService.checkBelong(oriBadGuest, propertyId);
            //只有approveing数据可以撤回
            if (oriBadGuest.getApproveStatus() != BlacklistApproveStatusEnum.APPROVEING.getStatus()) {
                throw new BadRequestException(CommonErrorI18nConstant.SPECIFIC_STATE_CAN_OPERATE,
                        i18nMessageUtils.getErrorParamMapNeedTranslate(BlacklistApproveStatusEnum.APPROVEING.getI18nCode(), BlacklistFieldI18nConstant.OPERATE_WITHDRAW),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "Only data approveing review can be withdrawn");
            }
            //黑名单的审批层级
            BadGuestApproveLevel approveLevel = badGuestApproveLevelService.getByIndexAndPropertyId(oriBadGuest.getApproveIndex(), propertyId);
            //当前层级的审批人
            List<Integer> userIdByApproval = badGuestApproveConfigService.listByLevelIds(propertyId, Lists.newArrayList(approveLevel.getId()))
                    .stream().map(BadGuestApproveConfig::getUserId).toList();
            //只有创建人 或 当前层级审批人 可以撤回数据
            boolean b = oriBadGuest.getCreatedBy() == loginUserId || userIdByApproval.contains(loginUserId);
            if (!b) {
                throw new BadRequestException(CommonErrorI18nConstant.SPECIFIC_USER_CAN_OPERATE,
                        i18nMessageUtils.getErrorParamMapNeedTranslate(List.of(BlacklistFieldI18nConstant.CREATOR, BlacklistFieldI18nConstant.APPROVAL_USER),
                                BlacklistFieldI18nConstant.OPERATE_WITHDRAW),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "Only creator and current approve level approver can withdrawn");
            }
            //撤回数据
            badGuestService.withdrawn(propertyDTO, oriBadGuest, opinion, loginUserId);
            return Collections.EMPTY_MAP;
        }

        /**
         * 送审
         *
         * @param id       主键id
         * @param property 资产标识
         * @return java.util.Map
         * @author lichen
         * @date 2025/8/26
         */
        @ApiId(161)
        @PutMapping("/toApprove/{id:\\d+}")
        public Map toApprove(@PathVariable("id") long id, String property) {
            int propertyId = sessionContext.getPropertyId(property);
            PropertyDTO propertyDTO = propertyI18nRedisService.get(propertyId);
            int loginUserId = sessionContext.getLoginUserId();

            //校验黑名单是否存在
            BadGuest oriBadGuest = badGuestService.checkExist(id);
            //校验黑名单是否归属于当前资产
            badGuestService.checkBelong(oriBadGuest, propertyId);
            //只有pending状态的数据可以送审
            if (oriBadGuest.getApproveStatus() != BlacklistApproveStatusEnum.PENDING.getStatus()) {
                throw new BadRequestException(CommonErrorI18nConstant.SPECIFIC_STATE_CAN_OPERATE,
                        i18nMessageUtils.getErrorParamMapNeedTranslate(BlacklistApproveStatusEnum.PENDING.getI18nCode(), BlacklistFieldI18nConstant.OPERATE_SUBMIT),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "only Pending data can to Approve");
            }
            //只有创建人可以送审
            checkCurrentUserIsHavePermission(oriBadGuest, loginUserId);
            //送审
            badGuestService.toApprove(propertyDTO, oriBadGuest, loginUserId);
            return Collections.EMPTY_MAP;
        }

        /**
         * 审核数据
         *
         * @param id         黑名单id
         * @param property   资产标识
         * @param approveDTO 审批对象
         * @return java.util.Map
         * @author lichen
         * @date 2025/8/26
         */
        @ApiId(162)
        @PutMapping("/approve/{id:\\d+}")
        public Map approve(@PathVariable("id") long id, String property,
                           @Validated @RequestBody ApproveDTO approveDTO) {
            int propertyId = sessionContext.getPropertyId(property);
            PropertyDTO propertyDTO = propertyI18nRedisService.get(propertyId);

            //校验黑名单是否存在
            BadGuest oriBadGuest = badGuestService.checkExist(id);
            //校验黑名单是否归属于当前资产
            badGuestService.checkBelong(oriBadGuest, propertyId);
            if (oriBadGuest.getApproveStatus() != BlacklistApproveStatusEnum.APPROVEING.getStatus()) {
                throw new BadRequestException(CommonErrorI18nConstant.SPECIFIC_STATE_CAN_OPERATE,
                        i18nMessageUtils.getErrorParamMapNeedTranslate(BlacklistApproveStatusEnum.APPROVEING.getI18nCode(), BlacklistFieldI18nConstant.OPERATE_APPROVAL),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "only Approveing data can Approve");
            }
            //审核数据
            badGuestService.approve(propertyDTO, oriBadGuest, approveDTO);
            return Collections.EMPTY_MAP;
        }

        /**
         * 导出数据
         *
         * @param tenant              租户
         * @param property            资产
         * @param exportBadGuestQuery 导出条件
         * @param response            http的响应对象
         * @return void
         * @author lichen
         * @date 2024/6/4
         */
        @ApiId(163)
        @GetMapping("/export")
        public void export(String tenant, String property, @Valid ExportBadGuestQuery exportBadGuestQuery, HttpServletResponse response) {
            //租户下的所有资产
            List<PropertyDTO> propertyDTOList = propertyInternalService.getPropertyByTenant(tenant);
            List<Integer> propertyIdsByTenant = propertyDTOList.stream().map(PropertyDTO::getId).toList();

            //查询的资产ID为空时，查询租户下所有资产的数据
            if (CollectionUtils.isEmpty(exportBadGuestQuery.getQueryPropertyIds())) {
                exportBadGuestQuery.setQueryPropertyIds(propertyIdsByTenant);
            } else {
                List<Integer> queryPropertyIds = exportBadGuestQuery.getQueryPropertyIds();
                if (queryPropertyIds.size() != Sets.newHashSet(queryPropertyIds).size()) {
                    throw new BadRequestException(CommonErrorI18nConstant.DATA_CANNOT_REPEAT,
                            i18nMessageUtils.getErrorParamMapNotNeedTranslate("propertyList"),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            "queryPropertyIds cannot repeat");
                }
                if (queryPropertyIds.stream().anyMatch(propertyId -> !propertyIdsByTenant.contains(propertyId))) {
                    throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                            BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(),
                            "The selected propertyId not belong current tenant");
                }
            }
            int propertyId = sessionContext.getPropertyId(property);
            PropertyDTO propertyDTO = propertyI18nRedisService.get(propertyId);
            //当前上下文语言
            Locale locale = LocaleContextHolder.getLocale();
            //查询导出的黑名单数据
            List<BadGuestExcelVO> excelVOList = badGuestService.listExport(exportBadGuestQuery, propertyDTO, propertyDTOList, locale);
            //模板文件名
            String templateFileName = StringUtils.join("tmpl-xls/DNR_tmpl_", LocaleContextHolder.getLocale(), ".xlsx");
            //导出Excel
            try (InputStream is = new ClassPathResource(templateFileName).getInputStream()) {
                HashMap<String, Object> data = new HashMap<>();
                data.put("badGuests", excelVOList);
                response.setContentType("application/vnd.ms-excel");
                response.addHeader("Access-Control-Expose-Headers", "Content-Disposition");
                response.setHeader("Content-Disposition", String.join("", "attachment; filename=", "blacklist.xlsx"));
                //填充模板
                JxlsPoiTemplateFillerBuilder.newInstance()
                        .withTemplate(is)
                        .buildAndFill(data, response::getOutputStream);
            } catch (IOException exp) {
                throw new InternalServerErrorException(BaseErrorCodeEnum.SYSTEM_ERROR.getI18nCode(),
                        BaseErrorCodeEnum.SYSTEM_ERROR.getCode(),
                        "IO exception occured when exporting adj excel file. property Id: " + propertyDTO.getId());
            }

        }

        /**
         * 查询报告处理日期
         *
         * @param property 资产标识
         * @return void
         * @author lichen
         * @date 2023/5/12
         */
        @ApiId(333)
        @GetMapping("/report-log")
        public List<ReportLogVO> getReportLog(String property) {
            int propertyId = sessionContext.getPropertyId(property);

            // 查询报告导入log
            List<BgReportLog> bgReportLogList = bgReportLogService.listByPropertyId(propertyId);

            //List<BgReportLog> -> Map<Integer, BgReportLog>
            Map<Integer, BgReportLog> reportType2BgReportLog = bgReportLogList.stream().collect(toMap(BgReportLog::getReportType, Function.identity()));
            //BgReportTypeEnum[] -> List<ReportLogVO>
            return Arrays.stream(BgReportTypeEnum.values()).map(bgReportTypeEnum -> {
                int reportType = bgReportTypeEnum.getType();
                BgReportLog bgReportLog = reportType2BgReportLog.get(reportType);
                Long processTime = Objects.nonNull(bgReportLog) ? bgReportLog.getProcessTime() : null;
                return new ReportLogVO(bgReportTypeEnum.getType(), processTime);
            }).collect(Collectors.toList());
        }

        /**
         * 导入报告
         *
         * @param property        资产标识
         * @param importReportDTO 导入报告的dto
         * @return java.util.Map<java.lang.String, java.lang.String>
         * @author lichen
         * @date 2025/7/26
         */
        @ApiId(334)
        @PostMapping("/import-report")
        public Map<String, String> importReport(String property, @Valid ImportReportDTO importReportDTO) {
            int propertyId = sessionContext.getPropertyId(property);
            PropertyDTO propertyDTO = propertyI18nRedisService.get(propertyId);
            int loginUserId = sessionContext.getLoginUserId();

            //校验文件大小
            if (importReportDTO.getFile().getSize() > importReportFileMaxSize) {
                throw new BadRequestException(CommonErrorI18nConstant.FILE_SIZE_ERROR,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "The fileSize max is " + importReportFileMaxSize);
            }
            String pmsType = propertyDTO.getPmsType();
            //校验source是否符合预期
            if (!Lists.newArrayList(pmsType, SystemPlatformEnum.OTHER.getDesc()).contains(importReportDTO.getSource())) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_INTEGRATION_SOURCE_SYSTEM_NOT_SUPPORTED,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The source must be in:" + pmsType + "|" + SystemPlatformEnum.OTHER.getDesc());
            }

            Collection<IParseFileService> values = applicationContext.getBeansOfType(IParseFileService.class).values();
            for (IParseFileService parser : values) {
                if (parser.supports(importReportDTO.getSource(), importReportDTO.getReportType())) {
                    //校验文件内容
                    parser.checkFile(importReportDTO.getFile());
                    //处理文件数据
                    parser.handleData(propertyDTO, importReportDTO.getFile(),
                            loginUserId, importReportDTO.getReportType());
                    break;
                }
            }
            return Collections.emptyMap();
        }

        /**
         * 删除黑名单数据
         *
         * @param id       黑名单id
         * @param property 资产
         * @return java.util.Map
         * @author lichen
         * @date 2024/6/4
         */
        @ApiId(1186)
        @DeleteMapping("/{id:\\d+}")
        public Map delete(@PathVariable("id") long id, String property) {
            int propertyId = sessionContext.getPropertyId(property);
            //校验黑名单是否存在
            BadGuest badGuest = badGuestService.checkExist(id);
            //校验黑名单是否归属于当前资产
            badGuestService.checkBelong(badGuest, propertyId);
            //只有待送审的数据可以删除
            if (badGuest.getApproveStatus() != BlacklistApproveStatusEnum.PENDING.getStatus()) {
                throw new BadRequestException(CommonErrorI18nConstant.SPECIFIC_STATE_CAN_OPERATE,
                        i18nMessageUtils.getErrorParamMapNeedTranslate(BlacklistApproveStatusEnum.PENDING.getI18nCode(), BlacklistFieldI18nConstant.OPERATE_DELETE),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "Only pending data can delete");
            }

            //删除数据
            badGuestService.remove(badGuest);
            return Collections.emptyMap();
        }

        /**
         * 补录黑名单用户信息
         *
         * @param property            资产
         * @param companionExtInfoDTO 追加的同伴信息
         * @return java.util.Map
         * @author lichen
         * @date 2024/6/4
         */
        @ApiId(1185)
        @PutMapping("/append")
        public Map updateCompanionExtInfo(String property, @RequestBody @Valid CompanionExtInfoDTO companionExtInfoDTO) {
            int propertyId = sessionContext.getPropertyId(property);
            PropertyDTO propertyDTO = propertyI18nRedisService.get(propertyId);
            int loginUserId = sessionContext.getLoginUserId();

            //校验黑名单同伴信息是否存在
            BadGuestCompanion badGuestCompanion = badGuestCompanionService.checkExist(companionExtInfoDTO.getId());
            //校验是否属于当前资产
            if (badGuestCompanion.getPropertyId().compareTo(propertyId) != 0) {
                throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(), BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(),
                        MessageFormat.format("BadGuestCompanion not belong currentProperty, BadGuestCompanionBelongPropertyId:{0}, currentPropertyId:{1}",
                                badGuestCompanion.getPropertyId(), propertyId));
            }
            //校验黑名单是否存在
            BadGuest badGuest = badGuestService.checkExist(badGuestCompanion.getBadGuestId());
            //校验黑名单是否归属于当前资产
            badGuestService.checkBelong(badGuest, propertyId);
            if (badGuest.getApproveStatus() != BlacklistApproveStatusEnum.APPROVED.getStatus()) {
                throw new BadRequestException(CommonErrorI18nConstant.SPECIFIC_STATE_CAN_OPERATE,
                        i18nMessageUtils.getErrorParamMapNeedTranslate(BlacklistApproveStatusEnum.APPROVED.getI18nCode(), BlacklistFieldI18nConstant.OPERATE_FILL_IN_GUEST_INFORMATION),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "only approved data can updateCompanionExtInfo");
            }
            //更新同伴扩展信息（手机号、地址、会员卡号、邮箱）
            badGuestCompanionService.updateCompanionExtInfo(propertyDTO, badGuestCompanion, companionExtInfoDTO, loginUserId);
            return Collections.emptyMap();
        }

        /**
         * 查询操作日志
         *
         * @param id 黑名单ID
         * @return java.util.List<cloud.wconnect.frontoffice.pojo.vo.badguest.BgOperateLogVO>
         * @author lichen
         * @date 2024/6/5
         */
        @ApiId(1189)
        @GetMapping("/{id:\\d+}/operate-log")
        public List<BgOperateLogVO> listOperateLog(@PathVariable("id") long id) {
            //校验黑名单是否存在
            BadGuest badGuest = badGuestService.checkExist(id);
            //查询黑名单归属的资产DTO
            PropertyDTO propertyDTO = propertyI18nRedisService.get(badGuest.getPropertyId());
            return operateLogService.list(propertyDTO, badGuest.getId());
        }

        /**
         * 查询黑名单备注信息
         *
         * @param badGuestId 黑名单id
         * @return java.util.List<cloud.wconnect.frontoffice.pojo.vo.badguest.BadGuestCommentVO>
         * @author lichen
         * @date 2024/6/17
         */
        @ApiId(1193)
        @GetMapping("/{badGuestId:\\d+}/comment")
        public List<BadGuestCommentVO> listComment(@PathVariable long badGuestId) {
            //校验黑名单是否存在
            BadGuest badGuest = badGuestService.checkExist(badGuestId);
            PropertyDTO propertyDTO = propertyI18nRedisService.get(badGuest.getPropertyId());
            //查询黑名单的备注信息
            List<BgComment> bgComments = commentService.listByBadGuestId(Collections.singletonList(badGuest.getId()));
            //查询用户信息
            Set<Integer> userIds = bgComments.stream().map(BgComment::getCreatedBy).collect(toSet());
            Map<Integer, UserDTO> userId2DTO = userInternalService.getUserMapByIds(propertyDTO.getTenantId(), userIds);
            return beanMapper.mapList(bgComments, BadGuestCommentVO.class, (entity, vo) -> {
                UserDTO userDTO = userId2DTO.getOrDefault(entity.getCreatedBy(), new UserDTO());
                //设置创建人姓名
                vo.setCreatedName(userDTO.getName());
                vo.setAvatar(userDTO.getAvatar());
            });
        }

        /**
         * 保存黑名单的备注信息
         *
         * @param badGuestId         黑名单id
         * @param cid                备注id
         * @param property           资产
         * @param badGuestCommentDTO 黑名单备注DTO
         * @return java.util.Map
         * @author lichen
         * @date 2024/6/17
         */
        @ApiId(1194)
        @PostMapping("/{badGuestId:\\d+}/comment/{cid:\\d+}")
        public Map saveComment(@PathVariable long badGuestId, @PathVariable long cid,
                               String property, @RequestBody @Valid BadGuestCommentDTO badGuestCommentDTO) {
            int propertyId = sessionContext.getPropertyId(property);
            //校验黑名单是否存在
            BadGuest badGuest = badGuestService.checkExist(badGuestId);
            //校验黑名单是否归属于当前资产
            badGuestService.checkBelong(badGuest, propertyId);
            if (cid > 0) {
                //校验备注
                BgComment oriComment = commentService.checkExist(cid);
                if (!oriComment.getBadGuestId().equals(badGuestId)) {
                    throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                            BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(),
                            MessageFormat.format("The comment is not belong to the current badGuest, badGuestId:{0}, commentBelongBadGuestId:{1}, commentId{2}",
                                    badGuestId, oriComment.getBadGuestId(), cid));
                }
                //校验只有创建人可以修改数据
                if (oriComment.getCreatedBy() != sessionContext.getLoginUserId()) {
                    throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                            BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(),
                            "Only the creator can modify the data");
                }
                //修改备注
                commentService.update(oriComment, badGuestCommentDTO);
                return ImmutableMap.of("id", cid);
            } else {//新增
                Long commentId = commentService.save(propertyId, badGuestId, badGuestCommentDTO, sessionContext.getLoginUserId());
                return ImmutableMap.of("id", commentId);
            }
        }

        /**
         * 删除黑名单
         *
         * @param badGuestId 黑名单id
         * @param cid        黑名单备注id
         * @param property   资产
         * @return java.util.Map
         * @author lichen
         * @date 2024/6/17
         */
        @ApiId(1195)
        @DeleteMapping("/{badGuestId:\\d+}/comment/{cid:\\d+}")
        public Map deleteComment(@PathVariable long badGuestId, @PathVariable long cid, String property) {
            int propertyId = sessionContext.getPropertyId(property);
            //校验黑名单是否存在
            BadGuest badGuest = badGuestService.checkExist(badGuestId);
            //校验黑名单是否归属于当前资产
            badGuestService.checkBelong(badGuest, propertyId);
            //校验备注
            BgComment oriComment = commentService.checkExist(cid);
            if (!oriComment.getBadGuestId().equals(badGuestId)) {
                throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                        BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(),
                        MessageFormat.format("The comment is not belong to the current badGuest, badGuestId:{0}, commentBelongBadGuestId:{1}, commentId{2}",
                                badGuestId, oriComment.getBadGuestId(), cid));
            }
            //校验只有创建人可以删除数据
            if (oriComment.getCreatedBy() != sessionContext.getLoginUserId()) {
                throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                        BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(),
                        "Only the creator can delete the data");
            }
            //删除
            commentService.remove(oriComment);
            return Collections.emptyMap();
        }

        /**
         * 黑名单取消
         *
         * @param id              黑名单id
         * @param property        资产
         * @param cancellationDTO 作废DTO
         * @return java.util.Map
         * @author lichen
         * @date 2024/6/17
         */
        @ApiId(1521)
        @PutMapping("/cancellation/{id:\\d+}")
        public Map cancellation(@PathVariable long id, String property, @RequestBody @Valid CancellationDTO cancellationDTO) {
            int propertyId = sessionContext.getPropertyId(property);
            PropertyDTO propertyDTO = propertyI18nRedisService.get(propertyId);
            //校验黑名单是否存在
            BadGuest badGuest = badGuestService.checkExist(id);
            //校验是否归属于当前资产
            badGuestService.checkBelong(badGuest, propertyId);
            //只有待送审和审批通过的数据可以作废
            if (!Arrays.asList(BlacklistApproveStatusEnum.PENDING.getStatus(), BlacklistApproveStatusEnum.APPROVED.getStatus()).contains(badGuest.getApproveStatus())) {
                throw new BadRequestException(CommonErrorI18nConstant.SPECIFIC_STATE_CAN_OPERATE,
                        i18nMessageUtils.getErrorParamMapNeedTranslate(List.of(BlacklistApproveStatusEnum.PENDING.getI18nCode(),
                                        BlacklistApproveStatusEnum.APPROVED.getI18nCode()),
                                BlacklistFieldI18nConstant.OPERATE_VOIDED),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "Only the pending or approved can cancellation the data");
            }
            //作废
            badGuestService.cancellation(propertyDTO, badGuest, cancellationDTO, sessionContext.getLoginUserId());
            return Collections.emptyMap();
        }

        /**
         * 校验当前用户是否有权限
         *
         * @param badGuest    黑名单
         * @param loginUserId 登录用户
         * @return void
         * @author lichen
         * @date 2023/11/11
         */
        private void checkCurrentUserIsHavePermission(BadGuest badGuest, int loginUserId) {
            Set<Integer> userIds = Sets.newHashSet(badGuest.getCreatedBy(), badGuest.getLastUpdatedBy());
            userIds.remove(0);
            if (!userIds.contains(loginUserId)) {
                throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(), BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(),
                        "Only the creator can submit the data");
            }
        }

    }