# LayoutController.java

    package cloud.wconnect.finance.controller.pl;

    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.exception.BadRequestException;
    import cloud.wconnect.commonlib.util.I18nMessageUtils;
    import cloud.wconnect.finance.cons.pl.PlFieldI18nConstant;
    import cloud.wconnect.finance.cons.pl.RevFormulaTypeEnum;
    import cloud.wconnect.finance.entity.pl.M3PlLayout;
    import cloud.wconnect.finance.entity.pl.M3Row;
    import cloud.wconnect.finance.pojo.dto.pl.M3PlLayoutDTO;
    import cloud.wconnect.finance.pojo.dto.pl.M3PlLayoutAbnormalDTO;
    import cloud.wconnect.finance.pojo.dto.pl.RevFormulaDTO;
    import cloud.wconnect.finance.service.pl.IM3PlLayoutService;
    import cloud.wconnect.finance.service.pl.IM3RowService;
    import cloud.wconnect.finance.service.pl.PlCommonService;
    import com.google.common.collect.ImmutableMap;
    import com.google.common.collect.Lists;
    import com.google.common.collect.Sets;
    import jakarta.validation.Valid;
    import org.apache.commons.lang3.StringUtils;
    import org.checkerframework.checker.nullness.qual.Nullable;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.*;

    import java.util.*;

    /**
     * layout配置控制层
     *
     * @author lichen
     * @date 2025/9/29
     */
    @RestController
    @RequestMapping("/pl/layout")
    public class LayoutController {

        @Autowired
        private SessionContext sessionContext;
        @Autowired
        private IM3PlLayoutService m3PlLayoutService;
        @Autowired
        private I18nMessageUtils i18nMessageUtils;
        @Autowired
        private IM3RowService m3RowService;
        @Autowired
        private PlCommonService plCommonService;

        /**
         * 获取layout配置
         *
         * @param property 资产标识
         * @param category 分类
         * @return java.util.List<cloud.wconnect.finance.pojo.dto.pl.M3PlLayoutAbnormalDTO>
         * @author lichen
         * @date 2025/9/30
         */
        @ApiId(1605)
        @GetMapping
        public List<M3PlLayoutAbnormalDTO> list(String property, @RequestParam(required = false) Integer category) {
            ArrayList<Integer> categoryList = Lists.newArrayList(M3PlLayout.CATEGORY_ROW, M3PlLayout.CATEGORY_BLANK,
                    M3PlLayout.CATEGORY_LABEL, M3PlLayout.CATEGORY_LINE);
            if (Objects.nonNull(category) && !categoryList.contains(category)) {
                throw new BadRequestException(CommonErrorI18nConstant.MUST_IN_LIST,
                        i18nMessageUtils.getErrorParamMapNotNeedTranslate("category", categoryList),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "category must between in [1:Row 2:Blank 3:Label 4:Line]");
            }
            int propertyId = sessionContext.getPropertyId(property);
            return m3PlLayoutService.listLayoutAbnormal(propertyId, category);
        }

        /**
         * 保存layout配置
         *
         * @param property      资产标识
         * @param id            主键id
         * @param m3PlLayoutDTO layout配置
         * @return java.util.Map<java.lang.String, java.lang.Object>
         * @author lichen
         * @date 2025/9/29
         */
        @ApiId(1606)
        @PostMapping("/{id:\\d+}")
        public Map<String, Object> save(String property, @PathVariable Integer id, @Valid @RequestBody M3PlLayoutDTO m3PlLayoutDTO) {
            int propertyId = sessionContext.getPropertyId(property);
            Integer tenantId = sessionContext.getTenantSymbolBO().getId();

            //校验字段
            checkField(m3PlLayoutDTO);

            //校验row是否存在
            if (m3PlLayoutDTO.getCategory() == M3PlLayout.CATEGORY_ROW) {
                HashSet<String> rowNumbersByUsed = Sets.newHashSetWithExpectedSize(4);
                rowNumbersByUsed.add(m3PlLayoutDTO.getRowNumber());
                rowNumbersByUsed.add(m3PlLayoutDTO.getPorFormulaDenominator());
                // %Rev公式
                RevFormulaDTO revFormulaDTO = m3PlLayoutDTO.getRevFormula();
                if (revFormulaDTO.getType() == RevFormulaTypeEnum.SUBTRACTION.getType()
                        || revFormulaDTO.getType() == RevFormulaTypeEnum.DIVISION.getType()) {
                    List<String> rowNumberList = plCommonService.parseFormulaUsedRowNumber(revFormulaDTO.getValue());
                    rowNumbersByUsed.addAll(rowNumberList);
                }
                List<M3Row> m3RowList = m3RowService.checkExist(Lists.newArrayList(rowNumbersByUsed), propertyId);
                m3RowService.checkBelong(m3RowList, propertyId);
            }

            if (id == 0) {
                if (m3PlLayoutDTO.getCategory() == M3PlLayout.CATEGORY_ROW) {
                    //校验rowNumber是否重复
                    m3PlLayoutService.checkRowNumberRepeat(propertyId, m3PlLayoutDTO.getRowNumber(), id);
                }
                return ImmutableMap.of("id", m3PlLayoutService.save(tenantId, propertyId, m3PlLayoutDTO));
            } else {
                //校验Layout是否存在
                M3PlLayout m3PlLayout = m3PlLayoutService.checkExist(id);
                //校验归属
                m3PlLayoutService.checkBelong(m3PlLayout, propertyId);

                if (m3PlLayoutDTO.getCategory() == M3PlLayout.CATEGORY_ROW) {
                    //校验rowNumber是否重复
                    m3PlLayoutService.checkRowNumberRepeat(propertyId, m3PlLayoutDTO.getRowNumber(), id);
                }
                //修改
                m3PlLayoutService.update(m3PlLayout, m3PlLayoutDTO);
                return ImmutableMap.of("id", id);
            }
        }

        /**
         * 删除layout配置
         *
         * @param property 资产标识
         * @param id       主键id
         * @return java.util.Map<java.lang.String, java.lang.Object>
         * @author lichen
         * @date 2025/9/29
         */
        @ApiId(1607)
        @DeleteMapping("/{id:\\d+}")
        public Map<String, Object> delete(String property, @PathVariable Integer id) {
            int propertyId = sessionContext.getPropertyId(property);
            //校验Layout是否存在
            M3PlLayout m3PlLayout = m3PlLayoutService.checkExist(id);
            //校验归属
            m3PlLayoutService.checkBelong(m3PlLayout, propertyId);
            if (m3PlLayout.getCategory() == M3PlLayout.CATEGORY_ROW) {
                //校验删除的Layout配置是否被其他%Rev减法公式依赖
                m3PlLayoutService.checkUsed(m3PlLayout);
            }

            //删除
            m3PlLayoutService.removeById(m3PlLayout.getId());
            return Collections.emptyMap();
        }

        /**
         * 校验字段
         *
         * @param m3PlLayoutDTO layout配置
         * @author lichen
         * @date 2025/9/29
         */
        private void checkField(M3PlLayoutDTO m3PlLayoutDTO) {
            List<Integer> styleTypeList = new ArrayList<>();
            //校验styleType格式是否合法
            String styleType = m3PlLayoutDTO.getStyleType();
            if (StringUtils.isNotBlank(styleType)) {
                if (!styleType.matches("^[0-9,]+$")) {
                    throw new BadRequestException(CommonErrorI18nConstant.FORMAT_ERROR,
                            i18nMessageUtils.getErrorParamMapNeedTranslate(PlFieldI18nConstant.STYLE_TYPE),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            "The styleType is invalid");
                } else {
                    styleTypeList = Arrays.stream(styleType.split(",")).map(Integer::parseInt).toList();
                }

            }

            if (m3PlLayoutDTO.getCategory() == M3PlLayout.CATEGORY_ROW) {
                //校验rowNumber不能为空
                if (Objects.isNull(m3PlLayoutDTO.getRowNumber())) {
                    throw new BadRequestException(CommonErrorI18nConstant.CANNOT_EMPTY,
                            i18nMessageUtils.getErrorParamMapNeedTranslate(PlFieldI18nConstant.ROW_NUMBER),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            "The rowNumber cannot be blank");
                }
                //校验styleType包含4时，styleValue不能为空
                if (styleTypeList.contains(M3PlLayout.STYLE_TYPE_FONT_COLOR)) {
                    if(StringUtils.isBlank(m3PlLayoutDTO.getStyleValue())){
                        throw new BadRequestException(CommonErrorI18nConstant.CANNOT_EMPTY,
                                i18nMessageUtils.getErrorParamMapNeedTranslate(PlFieldI18nConstant.STYLE_VALUE),
                                BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                                "The styleValue cannot be blank");
                    }else{
                        try{
                            Integer.parseInt(m3PlLayoutDTO.getStyleValue());
                        }catch (Exception e){
                            throw new BadRequestException(CommonErrorI18nConstant.MUST_INTEGER,
                                    i18nMessageUtils.getErrorParamMapNotNeedTranslate("styleValue"),
                                    BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                                    "The styleValue must integer, styleValue = " + m3PlLayoutDTO.getStyleValue());
                        }
                    }
                }
                //校验rowType不能为空
                if (Objects.isNull(m3PlLayoutDTO.getRowType())) {
                    throw new BadRequestException(CommonErrorI18nConstant.CANNOT_EMPTY,
                            i18nMessageUtils.getErrorParamMapNeedTranslate(PlFieldI18nConstant.ROW_TYPE),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            "The rowType cannot be blank");
                }
                //校验revFormula不能为空
                if (Objects.isNull(m3PlLayoutDTO.getRevFormula())) {
                    throw new BadRequestException(CommonErrorI18nConstant.CANNOT_EMPTY,
                            i18nMessageUtils.getErrorParamMapNeedTranslate(PlFieldI18nConstant.REV_FORMULA),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            "The revFormula cannot be blank");
                }
                //校验porFormulaDenominator不能为空
                if (Objects.isNull(m3PlLayoutDTO.getPorFormulaDenominator())) {
                    throw new BadRequestException(CommonErrorI18nConstant.CANNOT_EMPTY,
                            i18nMessageUtils.getErrorParamMapNeedTranslate(PlFieldI18nConstant.POR_FORMULA_DENOMINATOR),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            "The porFormulaDenominator cannot be blank");
                }
            } else if (m3PlLayoutDTO.getCategory() == M3PlLayout.CATEGORY_BLANK || m3PlLayoutDTO.getCategory() == M3PlLayout.CATEGORY_LINE) {
                //校验rowNumber必须为空
                if (Objects.nonNull(m3PlLayoutDTO.getRowNumber())) {
                    throw new BadRequestException(CommonErrorI18nConstant.MUST_EMPTY,
                            i18nMessageUtils.getErrorParamMapNeedTranslate(PlFieldI18nConstant.ROW_NUMBER),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            "The rowNumber must empty");
                }
                //校验styleType必须为空
                if (Objects.nonNull(m3PlLayoutDTO.getStyleType())) {
                    throw new BadRequestException(CommonErrorI18nConstant.MUST_EMPTY,
                            i18nMessageUtils.getErrorParamMapNeedTranslate(PlFieldI18nConstant.STYLE_TYPE),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            "The styleType must empty");
                }
                //校验styleValue必须为空
                if (Objects.nonNull(m3PlLayoutDTO.getStyleValue())) {
                    throw new BadRequestException(CommonErrorI18nConstant.MUST_EMPTY,
                            i18nMessageUtils.getErrorParamMapNeedTranslate(PlFieldI18nConstant.STYLE_VALUE),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            "The styleValue must empty");
                }
                //校验rowType必须为空
                if (Objects.nonNull(m3PlLayoutDTO.getRowType())) {
                    throw new BadRequestException(CommonErrorI18nConstant.MUST_EMPTY,
                            i18nMessageUtils.getErrorParamMapNeedTranslate(PlFieldI18nConstant.ROW_TYPE),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            "The rowType must empty");
                }
                //校验revFormula必须为空
                if (Objects.nonNull(m3PlLayoutDTO.getRevFormula())) {
                    throw new BadRequestException(CommonErrorI18nConstant.MUST_EMPTY,
                            i18nMessageUtils.getErrorParamMapNeedTranslate(PlFieldI18nConstant.REV_FORMULA),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            "The revFormula must empty");
                }
                //校验porFormulaDenominator必须为空
                if (Objects.nonNull(m3PlLayoutDTO.getPorFormulaDenominator())) {
                    throw new BadRequestException(CommonErrorI18nConstant.MUST_EMPTY,
                            i18nMessageUtils.getErrorParamMapNeedTranslate(PlFieldI18nConstant.POR_FORMULA_DENOMINATOR),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            "The porFormulaDenominator must empty");
                }
            } else {
                //校验rowNumber不能为空
                if (Objects.isNull(m3PlLayoutDTO.getRowNumber())) {
                    throw new BadRequestException(CommonErrorI18nConstant.CANNOT_EMPTY,
                            i18nMessageUtils.getErrorParamMapNeedTranslate(PlFieldI18nConstant.ROW_NUMBER),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            "The rowNumber cannot be blank");
                }
                //校验styleType等于4时，styleValue不能为空
                if (styleTypeList.contains(M3PlLayout.STYLE_TYPE_FONT_COLOR) && Objects.isNull(m3PlLayoutDTO.getStyleValue())) {
                    throw new BadRequestException(CommonErrorI18nConstant.CANNOT_EMPTY,
                            i18nMessageUtils.getErrorParamMapNeedTranslate(PlFieldI18nConstant.STYLE_VALUE),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            "The styleValue cannot be blank");
                }

                //校验rowType必须为空
                if (Objects.nonNull(m3PlLayoutDTO.getRowType())) {
                    throw new BadRequestException(CommonErrorI18nConstant.MUST_EMPTY,
                            i18nMessageUtils.getErrorParamMapNeedTranslate(PlFieldI18nConstant.ROW_TYPE),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            "The rowType must empty");
                }
                //校验revFormula必须为空
                if (Objects.nonNull(m3PlLayoutDTO.getRevFormula())) {
                    throw new BadRequestException(CommonErrorI18nConstant.MUST_EMPTY,
                            i18nMessageUtils.getErrorParamMapNeedTranslate(PlFieldI18nConstant.REV_FORMULA),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            "The revFormula must empty");
                }
                //校验porFormulaDenominator必须为空
                if (Objects.nonNull(m3PlLayoutDTO.getPorFormulaDenominator())) {
                    throw new BadRequestException(CommonErrorI18nConstant.MUST_EMPTY,
                            i18nMessageUtils.getErrorParamMapNeedTranslate(PlFieldI18nConstant.POR_FORMULA_DENOMINATOR),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            "The porFormulaDenominator must empty");
                }
            }
        }

    }