# ArrivalController.java

    package cloud.wconnect.frontoffice.controller.clc;

    import cloud.wconnect.commonlib.annotation.ApiId;
    import cloud.wconnect.commonlib.component.BeanMapper;
    import cloud.wconnect.commonlib.component.SessionContext;
    import cloud.wconnect.commonlib.cons.*;
    import cloud.wconnect.commonlib.exception.BadRequestException;
    import cloud.wconnect.commonlib.exception.ForbiddenException;
    import cloud.wconnect.commonlib.pojo.bo.SymbolBO;
    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.I18nMessageUtils;
    import cloud.wconnect.frontoffice.cons.clc.ClcErrorCons;
    import cloud.wconnect.frontoffice.entity.clc.ClcArrivalDetailed;
    import cloud.wconnect.frontoffice.entity.clc.ClcMissingReport;
    import cloud.wconnect.frontoffice.entity.clc.ClcSiteData;
    import cloud.wconnect.frontoffice.pojo.dto.clc.*;
    import cloud.wconnect.frontoffice.pojo.query.clc.ArrivalImportLogQuery;
    import cloud.wconnect.frontoffice.pojo.query.clc.ArrivalQuery;
    import cloud.wconnect.frontoffice.service.clc.IClcArrivalDetailedService;
    import cloud.wconnect.frontoffice.service.clc.IClcMissingReportService;
    import cloud.wconnect.frontoffice.service.clc.IClcSiteDataService;
    import cloud.wconnect.frontoffice.service.clc.arrival.IArrivalFileParse;
    import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    import com.google.common.collect.Lists;
    import com.google.common.collect.Maps;
    import jakarta.validation.Valid;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationContext;
    import org.springframework.web.bind.annotation.*;
    import org.springframework.web.multipart.MultipartFile;

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

    /**
     * 控制层：Arrival
     *
     * @author yyt
     * 2023/7/24
     */
    @RestController
    @RequestMapping("/clc/arrival-detail")
    public class ArrivalController {

        @Autowired
        private IClcArrivalDetailedService clcArrivalDetailedService;
        @Autowired
        private IClcSiteDataService clcSiteDataService;
        @Autowired
        private IClcMissingReportService clcMissingReportService;
        @Autowired
        private PropertyI18nRedisService propertyI18nRedisService;
        @Autowired
        private SessionContext sessionContext;
        @Autowired
        private BeanMapper beanMapper;
        @Autowired
        private ApplicationContext applicationContext;
        @Autowired
        private I18nMessageUtils i18nMessageUtils;

        /**
         * 获取 arrival 列表数据
         *
         * @param property 资产标识
         * @param query    查询条件
         * @return arrival 列表
         * @author yyt
         * @date 2023/7/24
         */
        @ApiId(477)
        @GetMapping
        public ListPagedDTO<ArrivalDTO> listArrival(String property, ArrivalQuery query) {
            Integer propertyId = sessionContext.getPropertyId(property);
            Page<ClcArrivalDetailed> page = clcArrivalDetailedService.listPage(propertyId, query);
            return new ListPagedDTO<>(page.getTotal(), beanMapper.mapList(page.getRecords(), ArrivalDTO.class));
        }

        /**
         * 统计 arrival 各状态数据
         *
         * @param property 资产标识
         * @param query    查询条件
         * @return arrival 各状态数据
         * @author yyt
         * @date 2023/7/24
         */
        @ApiId(478)
        @GetMapping("/statistics")
        public Map<Integer, Long> countArrival(String property, ArrivalQuery query) {
            List<ArrivalCountDTO> arrivalCountDTOS = clcArrivalDetailedService.countArrival(sessionContext.getPropertyId(property), query);
            Map<Integer, Long> result = Maps.newHashMapWithExpectedSize(arrivalCountDTOS.size());
            arrivalCountDTOS.forEach(item -> result.put(item.getMatchResult(), item.getCount()));
            return result;
        }

        /**
         * 查询 arrival 详情
         *
         * @param id       arrival id
         * @param property 资产标识
         * @return arrival 详情
         * @author yyt
         * @date 2023/7/24
         */
        @ApiId(479)
        @GetMapping("/{id:\\d+}")
        public Map<String, Object> detail(@PathVariable long id, String property) {
            Integer propertyId = sessionContext.getPropertyId(property);
            ClcArrivalDetailed clcArrivalDetailed = clcArrivalDetailedService.checkExist(id);
            checkBelong(clcArrivalDetailed, propertyId);

            ArrivalDTO arrivalDTO = beanMapper.map(clcArrivalDetailed, ArrivalDTO.class);
            Map<String, Object> result = Maps.newHashMapWithExpectedSize(2);
            result.put("opArrivDetailed", arrivalDTO);

            List<ClcSiteData> clcSiteData = clcSiteDataService.list(propertyId, clcArrivalDetailed.getArrivDate());
            ClcSiteData siteData = clcSiteDataService.getMatchRecord(clcArrivalDetailed, clcSiteData, false);
            if (Objects.nonNull(siteData)) {
                result.put("clcArrivDetailed", beanMapper.map(siteData, SiteDataDTO.class));
            }

            return result;
        }

        /**
         * 处理 arrival 数据
         *
         * @param id                     arrival id
         * @param property               资产标识
         * @param updateArrivalStatusDTO 修改状态信息
         * @return {}
         * @author yyt
         * @date 2023/7/25
         */
        @ApiId(480)
        @PutMapping("/{id:\\d+}")
        public Map handleArrival(@PathVariable long id, String property,
                                 @Valid @RequestBody UpdateArrivalStatusDTO updateArrivalStatusDTO) {
            Integer propertyId = sessionContext.getPropertyId(property);
            ClcArrivalDetailed clcArrivalDetailed = clcArrivalDetailedService.checkExist(id);
            checkBelong(clcArrivalDetailed, propertyId);

            if (!Lists.newArrayList(ClcArrivalDetailed.MATCH_RESULT_MISSED, ClcArrivalDetailed.MATCH_RESULT_CLC_ERR)
                    .contains(clcArrivalDetailed.getMatchResult())) {
                throw new BadRequestException(CommonErrorI18nConstant.MUST_IN_LIST,
                        i18nMessageUtils.getErrorParamMapNeedTranslate(CommonFieldI18nConstant.STATUS, Lists.newArrayList(ClcErrorCons.MISSED, ClcErrorCons.CLC_ERR)),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "the arrivalDetail status is error");
            }

            clcArrivalDetailedService.update(id, updateArrivalStatusDTO, sessionContext.getLoginUserId());

            return Collections.emptyMap();
        }

        /**
         * 解决 arrival 异常
         *
         * @param property  资产标识
         * @param id        异常 id
         * @param file      报告文件
         * @param importDTO 选择条件
         * @return {}
         * @author yyt
         * @date 2023/7/25
         */
        @ApiId(482)
        @PutMapping("/exception/{id:\\d+}")
        public Map resolveException(String property, @PathVariable long id, MultipartFile file, ImportArrivalDTO importDTO) {
            ClcMissingReport missingReport = clcMissingReportService.checkExist(id);
            if (!Lists.newArrayList(ReportImportResultCons.IMPORT_RESULT_FAIL, ReportImportResultCons.IMPORT_RESULT_MISSED)
                    .contains(missingReport.getStatus())) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_INTEGRATION_ONLY_HANDLE_EXCEPTION,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "exception handled Non-repeatable operation");
            }

            int userId = sessionContext.getLoginUserId();

            if (Objects.isNull(file)) {
                // 忽略该异常
                clcMissingReportService.updateStatus(id, ReportImportResultCons.IMPORT_RESULT_IGNORE, userId);
            } else {
                if (StringUtils.isBlank(importDTO.getSource())) {
                    throw new BadRequestException(CommonErrorI18nConstant.CANNOT_EMPTY,
                            i18nMessageUtils.getErrorParamMapNeedTranslate("source"),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "source cannot be blank");
                }

                //校验source是否符合预期
                PropertyDTO propertyDTO = propertyI18nRedisService.get(sessionContext.getPropertyId(property));
                checkSource(propertyDTO, importDTO.getSource());

                SymbolBO tenant = sessionContext.getTenantSymbolBO();
                importDTO.setTheDate(missingReport.getTheDate());

                Map<String, IArrivalFileParse> beans = applicationContext.getBeansOfType(IArrivalFileParse.class);
                for (IArrivalFileParse bean : beans.values()) {
                    if (bean.supports(importDTO.getSource())) {
                        bean.checkFile(propertyDTO, file);

                        ClcMissingReport importLog = bean.handleImportLog(tenant, propertyDTO, missingReport,
                                importDTO.getTheDate(), file, userId);

                        bean.handleFile(tenant, propertyDTO, file, importLog, userId);
                        return Collections.emptyMap();
                    }
                }
            }

            return Collections.emptyMap();
        }

        /**
         * 手动导入 arrival 报告
         *
         * @param property         资产标识
         * @param file             报告
         * @param importArrivalDTO 选择条件
         * @return {}
         * @author yyt
         * @date 2023/7/27
         */
        @ApiId(483)
        @PostMapping("/file")
        public Map importArrivalReport(String property, MultipartFile file, @Valid ImportArrivalDTO importArrivalDTO) {
            SymbolBO tenant = sessionContext.getTenantSymbolBO();
            PropertyDTO propertyDTO = propertyI18nRedisService.get(sessionContext.getPropertyId(property));
            int userId = sessionContext.getLoginUserId();

            //校验source是否符合预期
            checkSource(propertyDTO, importArrivalDTO.getSource());

            Map<String, IArrivalFileParse> beans = applicationContext.getBeansOfType(IArrivalFileParse.class);
            for (IArrivalFileParse bean : beans.values()) {
                if (bean.supports(importArrivalDTO.getSource())) {
                    bean.checkFile(propertyDTO, file);

                    ClcMissingReport importLog = bean.handleImportLog(tenant, propertyDTO, null,
                            importArrivalDTO.getTheDate(), file, userId);

                    bean.handleFile(tenant, propertyDTO, file, importLog, userId);
                    return Collections.emptyMap();
                }
            }

            return Collections.emptyMap();
        }

        /**
         * 获取 arrival 导入记录
         *
         * @param property 资产标识
         * @param query    查询条件
         * @return arrival 导入记录
         * @author yyt
         * @date 2023/7/25
         */
        @ApiId(484)
        @GetMapping("/import-log")
        public ListPagedDTO<ImportArrivalLogDTO> listImportArrivalLog(String property, @Valid ArrivalImportLogQuery query) {
            return clcMissingReportService.listImportArrivalLogDTO(sessionContext.getTenantSymbolBO().getId(), sessionContext.getPropertyId(property), query);
        }

        /**
         * 校验数据归属
         *
         * @param clcArrivalDetailed arrival
         * @param propertyId         资产 id
         * @author yyt
         * @date 2023/7/24
         */
        private void checkBelong(ClcArrivalDetailed clcArrivalDetailed, Integer propertyId) {
            if (!clcArrivalDetailed.getPropertyId().equals(propertyId)) {
                throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                        BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(), "the property id parameter is different from " +
                        "the property id of the clcArrivalDetailed;propertyId:" + propertyId + ",clcArrivalDetailedPropertyId:" +
                        clcArrivalDetailed.getPropertyId() + ",clcArrivalDetailedId:" + clcArrivalDetailed.getId());
            }
        }

        /**
         * 校验source是否符合预期
         *
         * @param propertyDTO 资产DTO
         * @param source      来源
         * @return void
         * @author lichen
         * @date 2025/7/28
         */
        private void checkSource(PropertyDTO propertyDTO, String source) {
            String pmsType = propertyDTO.getPmsType();
            //校验source是否符合预期
            if (!Lists.newArrayList(pmsType, SystemPlatformEnum.OTHER.getDesc()).contains(source)) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_INTEGRATION_SOURCE_SYSTEM_NOT_SUPPORTED,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The source must be in:" + pmsType + "|" + SystemPlatformEnum.OTHER.getDesc());
            }
        }

    }