# SalesKpiConfigController.java

    package cloud.wconnect.workperformance.controller.saleskpi.config;

    import cloud.wconnect.commonlib.annotation.ApiId;
    import cloud.wconnect.commonlib.component.SessionContext;
    import cloud.wconnect.commonlib.cons.BaseErrorCodeEnum;
    import cloud.wconnect.commonlib.exception.BadRequestException;
    import cloud.wconnect.commonlib.pojo.dto.ListPagedDTO;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.pojo.dto.jsonview.Views;
    import cloud.wconnect.commonlib.redis.PropertyI18nRedisService;
    import cloud.wconnect.workperformance.cons.saleskpi.SalesKpiErrorI18nConstant;
    import cloud.wconnect.workperformance.entity.saleskpi.SalesKpiConfig;
    import cloud.wconnect.workperformance.entity.saleskpi.SalesKpiMonthlyConfig;
    import cloud.wconnect.workperformance.pojo.dto.common.FirstDayDTO;
    import cloud.wconnect.workperformance.pojo.dto.saleskpi.config.*;
    import cloud.wconnect.workperformance.pojo.dto.saleskpi.monthly.basic.MonthlyConfigItemDTO;
    import cloud.wconnect.workperformance.pojo.dto.saleskpi.monthly.revpar.RevparDTO;
    import cloud.wconnect.workperformance.pojo.query.saleskpi.SalesConfigQuery;
    import cloud.wconnect.workperformance.service.saleskpi.*;
    import com.fasterxml.jackson.annotation.JsonView;
    import com.google.common.collect.ImmutableMap;
    import jakarta.annotation.Resource;
    import jakarta.validation.Valid;
    import org.apache.commons.collections.CollectionUtils;
    import org.apache.commons.compress.utils.Lists;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.validation.annotation.Validated;
    import org.springframework.web.bind.annotation.*;

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

    /**
     * <p>
     * salesman  kpi config 前端控制器
     * </p>
     *
     * @author zc
     * @since 2023-09-05
     */
    @Validated
    @RestController
    @RequestMapping("/sales-kpi/config")
    public class SalesKpiConfigController {

        @Resource
        private ISalesKpiConfigService salesKpiConfigService;
        @Resource
        private ISalesKpiMonthlyConfigService salesKpiMonthlyConfigService;
        @Resource
        private ISalesKpiMonthlyRevenueConfigService salesKpiMonthlyRevenueConfigService;
        @Resource
        private ISalesKpiFirstDayLogService salesKpiFirstDayLogService;
        @Resource
        private ISalesKpiRevParSourceConfigService revParSourceConfigService;
        @Resource
        private PropertyI18nRedisService propertyI18nRedisService;
        @Resource
        private SessionContext sessionContext;

        /**
         * 获取配置列表
         *
         * @param property 资产标识
         * @param query    查询条件
         * @return SalesConfigVO 集合
         * @author zc
         * @date 2023/9/5
         */
        @ApiId(635)
        @GetMapping
        @JsonView(Views.ListView.class)
        public ListPagedDTO<SalesConfigDTO> list(String property, SalesConfigQuery query) {
            int propertyId = sessionContext.getPropertyId(property);
            PropertyDTO propertyDTO = propertyI18nRedisService.get(propertyId);
            return salesKpiConfigService.list(propertyDTO, query);
        }

        /**
         * 保存 segments
         *
         * @param property 资产标识
         * @param id       Sales config id
         * @param segments segments
         * @return id 集合
         * @author zc
         * @date 2023/9/19
         */
        @ApiId(699)
        @PostMapping("/{id:\\d+}/segments")
        public Map<Object, Object> saveSegments(String property, @PathVariable int id,
                                                @RequestBody List<@Valid MonthlyConfigItemDTO> segments) {
            int propertyId = sessionContext.getPropertyId(property);

            // 校验配置数据是否存在
            SalesKpiConfig originConfig = salesKpiConfigService.checkExist(id);

            // 校验配置数据归属
            salesKpiConfigService.checkBelong(propertyId, originConfig);

            // 保存 segment 指标
            salesKpiConfigService.saveSegments(propertyId, originConfig, segments, sessionContext.getLoginUserId());

            return Collections.emptyMap();
        }

        /**
         * 添加配置
         *
         * @param property       资产标识
         * @param salesConfigDTO 配置数据
         * @return id
         * @author zc
         * @date 2023/9/6
         */
        @ApiId(636)
        @PostMapping
        public Map<String, Integer> add(String property, @RequestBody @Valid SalesConfigDTO salesConfigDTO) {
            int tenantId = sessionContext.getTenantSymbolBO().getId();
            int propertyId = sessionContext.getPropertyId(property);

            // 校验业务员是否有效
            salesKpiConfigService.checkSalesmanIdEffective(tenantId, propertyId, salesConfigDTO.getSalesmanId());
            // 校验业务员配置数据是否重复
            salesKpiConfigService.checkSalesmanAlreadyExist(propertyId, salesConfigDTO.getSalesmanId(), null);
            // 校验业务员账号是否重复
            salesKpiConfigService.checkSalesAccountAlreadyExist(propertyId, salesConfigDTO.getSalesAccount(), null);

            // 校验设置的 Monthly Target 是否有效
            if (CollectionUtils.isNotEmpty(salesConfigDTO.getMonthlyTargets())) {
                List<Integer> itemIdList = salesConfigDTO.getMonthlyTargets().stream()
                        .map(SalesConfigMonthlyTargetDTO::getItemId).toList();
                salesKpiMonthlyConfigService.checkMonthlyItemEffective(propertyId, itemIdList,
                        SalesKpiMonthlyConfig.TYPE_GENERAL);
            }

            // 校验设置隐藏的基本指标是否有效
            if (CollectionUtils.isNotEmpty(salesConfigDTO.getHiddenItemIds())) {
                salesKpiMonthlyConfigService.checkMonthlyItemEffective(propertyId, salesConfigDTO.getHiddenItemIds(),
                        SalesKpiMonthlyConfig.TYPE_GENERAL);
            }

            // 保存配置数据
            return ImmutableMap.of("id", salesKpiConfigService.add(propertyId,
                    sessionContext.getLoginUserId(), salesConfigDTO));
        }

        /**
         * 编辑配置
         *
         * @param id             配置 id
         * @param property       资产标识
         * @param salesConfigDTO 配置数据
         * @return empty map
         * @author zc
         * @date 2023/9/6
         */
        @ApiId(650)
        @PutMapping("/{id:\\d+}")
        public Map<Object, Object> update(@PathVariable int id, String property,
                                          @RequestBody @Valid SalesConfigDTO salesConfigDTO) {
            int tenantId = sessionContext.getTenantSymbolBO().getId();
            int propertyId = sessionContext.getPropertyId(property);

            // 校验配置数据是否存在
            SalesKpiConfig originConfig = salesKpiConfigService.checkExist(id);
            // 校验配置数据归属
            salesKpiConfigService.checkBelong(propertyId, originConfig);
            // 校验业务员是否有效
            salesKpiConfigService.checkSalesmanIdEffective(tenantId, propertyId, salesConfigDTO.getSalesmanId());
            // 校验业务员配置数据是否重复
            salesKpiConfigService.checkSalesmanAlreadyExist(propertyId, salesConfigDTO.getSalesmanId(), id);
            // 校验业务员账号是否重复
            salesKpiConfigService.checkSalesAccountAlreadyExist(propertyId, salesConfigDTO.getSalesAccount(), id);

            // 校验设置的 Monthly Target 是否有效
            List<Integer> itemIdList = salesConfigDTO.getMonthlyTargets().stream()
                    .map(SalesConfigMonthlyTargetDTO::getItemId).toList();
            checkMonthlyTargetItem(propertyId, originConfig.getMarketSegments(), itemIdList);

            // 校验设置隐藏的基本指标是否有效
            if (CollectionUtils.isNotEmpty(salesConfigDTO.getHiddenItemIds())) {
                salesKpiMonthlyConfigService.checkMonthlyItemEffective(propertyId, salesConfigDTO.getHiddenItemIds(),
                        SalesKpiMonthlyConfig.TYPE_GENERAL);
            }
            // 编辑配置数据
            salesKpiConfigService.update(id, propertyId, sessionContext.getLoginUserId(), salesConfigDTO);
            return Collections.emptyMap();
        }

        /**
         * 校验设置的 Monthly Target 是否有效
         *
         * @param propertyId       资产 id
         * @param marketSegments   segment
         * @param monthlyTargetIds monthly target item id
         * @author zc
         * @date 2023/9/21
         */
        private void checkMonthlyTargetItem(int propertyId, String marketSegments, List<Integer> monthlyTargetIds) {
            if (CollectionUtils.isEmpty(monthlyTargetIds)) {
                return;
            }
            List<Integer> monthlyConfigIds = Lists.newArrayList();
            // 获取 segment 指标 id
            monthlyConfigIds.addAll(Stream.of(StringUtils.split(marketSegments, ",")).map(Integer::parseInt).toList());
            // 获取基本指标 id
            monthlyConfigIds.addAll(salesKpiMonthlyConfigService.getMonthlyConfigs(propertyId,
                            SalesKpiMonthlyConfig.TYPE_GENERAL, Boolean.FALSE).stream()
                    .map(SalesKpiMonthlyConfig::getId).toList());
            if (monthlyTargetIds.stream().anyMatch(item -> !monthlyConfigIds.contains(item))) {
                throw new BadRequestException(SalesKpiErrorI18nConstant.ITEM_CHANGED,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The itemIds validation error; itemIds:" + monthlyTargetIds);
            }
        }

        /**
         * 删除配置
         *
         * @param id       配置 id
         * @param property 资产标识
         * @return empty map
         * @author zc
         * @date 2023/9/7
         */
        @ApiId(637)
        @DeleteMapping("/{id:\\d+}")
        public Map<Object, Object> delete(@PathVariable int id, String property) {
            int propertyId = sessionContext.getPropertyId(property);

            // 校验配置数据是否存在
            SalesKpiConfig originConfig = salesKpiConfigService.checkExist(id);

            // 校验配置数据归属
            salesKpiConfigService.checkBelong(propertyId, originConfig);

            // 删除配置数据
            salesKpiConfigService.removeById(id);

            return Collections.emptyMap();
        }

        /**
         * 修改配置状态
         *
         * @param id              配置 id
         * @param property        资产标识
         * @param updateStatusDTO 状态
         * @return empty map
         * @author zc
         * @date 2023/9/7
         */
        @ApiId(638)
        @PutMapping("/{id:\\d+}/status")
        public Map<Object, Object> updateStatus(@PathVariable int id, String property,
                                                @RequestBody @Valid SalesConfigUpdateStatusDTO updateStatusDTO) {
            int propertyId = sessionContext.getPropertyId(property);
            // 校验配置数据是否存在
            SalesKpiConfig originConfig = salesKpiConfigService.checkExist(id);
            // 校验配置数据归属
            salesKpiConfigService.checkBelong(propertyId, originConfig);
            // 修改配置状态
            salesKpiConfigService.updateById(SalesKpiConfig.builder()
                    .id(id).status(updateStatusDTO.getStatus())
                    .build());
            return Collections.emptyMap();
        }

        /**
         * 批量修改 Weekly 配置指标
         *
         * @param property        资产标识
         * @param updateTargetDTO 指标数据
         * @return empty map
         * @author zc
         * @date 2023/9/7
         */
        @ApiId(639)
        @PutMapping("/weekly-target")
        public Map<Object, Object> updateWeeklyTargetBatch(String property, @RequestBody @Valid SalesConfigUpdateTargetDTO updateTargetDTO) {
            int propertyId = sessionContext.getPropertyId(property);
            // 批量修改 Weekly 配置指标
            salesKpiConfigService.updateWeeklyTargetBatch(propertyId, updateTargetDTO);
            return Collections.emptyMap();
        }

        /**
         * 获取需要提取月报功能中Room Revenue数据的分类名称配置
         *
         * @param property 资产标识
         * @return SalesConfigMonthlyRoomRevenueDTO
         * @author zc
         * @date 2023/9/7
         */
        @ApiId(648)
        @GetMapping("/monthly-room-revenue")
        public SalesConfigMonthlyRoomRevenueDTO getMonthlyRoomRevenueConfig(String property) {
            int propertyId = sessionContext.getPropertyId(property);
            SalesConfigMonthlyRoomRevenueDTO roomRevenueDTO = salesKpiMonthlyRevenueConfigService.getByPropertyId(propertyId);
            if (Objects.isNull(roomRevenueDTO)) {
                return new SalesConfigMonthlyRoomRevenueDTO();
            }
            return roomRevenueDTO;
        }

        /**
         * 设置需要提取月报功能中Room Revenue数据的分类名称
         *
         * @param property 资产标识
         * @return SalesConfigMonthlyRoomRevenueDTO
         * @author zc
         * @date 2023/9/7
         */
        @ApiId(649)
        @PutMapping("/monthly-room-revenue")
        public Map<Object, Object> saveMonthlyRoomRevenueConfig(String property, @RequestBody @Valid SalesConfigMonthlyRoomRevenueDTO roomRevenueDTO) {
            int propertyId = sessionContext.getPropertyId(property);
            int loginUserId = sessionContext.getLoginUserId();
            salesKpiMonthlyRevenueConfigService.saveMonthlyRoomRevenueConfig(propertyId, roomRevenueDTO, loginUserId);
            return Collections.emptyMap();
        }

        /**
         * 获取周起始日
         *
         * @param property 资产标识
         * @return 周起始日（1:Mon, 2:Tue, 3:Wed, 4:Thu, 5:Fri, 6:Sat, 7:Sun）
         * @author zc
         * @date 2023/9/8
         */
        @ApiId(651)
        @GetMapping("/first-day")
        public Map<String, Integer> getFirstDay(String property) {
            int propertyId = sessionContext.getPropertyId(property);
            return ImmutableMap.of("firstDay", salesKpiFirstDayLogService.getFirstDay(propertyId));
        }

        /**
         * 设置周起始日
         *
         * @param property    资产标识
         * @param firstDayDTO 周起始日
         * @return empty map
         * @author zc
         * @date 2023/9/8
         */
        @ApiId(652)
        @PostMapping("/first-day")
        public Map<Object, Object> saveFirstDay(String property, @RequestBody @Valid FirstDayDTO firstDayDTO) {
            int propertyId = sessionContext.getPropertyId(property);
            salesKpiFirstDayLogService.saveFirstDay(propertyId, sessionContext.getLoginUserId(), firstDayDTO);
            return Collections.emptyMap();
        }

        /**
         * 1203 - 获取 revpar 指标取值配置
         *
         * @param property 资产表锁
         * @return 取值配置
         * @author zc
         * @date 2025/7/31
         */
        @ApiId(1203)
        @GetMapping("/revpar")
        public Map<String, Object> getRevParConfig(String property) {
            int propertyId = sessionContext.getPropertyId(property);
            return ImmutableMap.of("source", revParSourceConfigService.getByPropertyId(propertyId).getSource());
        }

        /**
         * 1204 - 修改 revpar 指标取值配置
         *
         * @param property  资产标识
         * @param revparDTO 数据源 1: Monthly Report; 2: Crawl
         * @return java.util.Map<java.lang.Object, java.lang.Object>
         * @author zc
         * @date 2024/6/13
         */
        @ApiId(1204)
        @PostMapping("/revpar")
        public Map<Object, Object> saveRevParConfig(@RequestParam String property, @Valid @RequestBody RevparDTO revparDTO) {
            int propertyId = sessionContext.getPropertyId(property);
            revParSourceConfigService.save(propertyId, revparDTO.getSource(), sessionContext.getLoginUserId());
            return Collections.emptyMap();
        }

    }