# FdProcedureCheckController.java

    package cloud.wconnect.workperformance.controller.fdprocedurecheck;

    import cloud.wconnect.commonlib.annotation.ApiId;
    import cloud.wconnect.commonlib.component.SessionContext;
    import cloud.wconnect.commonlib.cons.BaseConstant;
    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.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.service.UserInternalService;
    import cloud.wconnect.commonlib.util.I18nMessageUtils;
    import cloud.wconnect.workperformance.cons.fdprocedurecheck.FdProcedureCheckErrorI18nConstant;
    import cloud.wconnect.workperformance.cons.fdprocedurecheck.FdProcedureCheckHandledResultEnum;
    import cloud.wconnect.workperformance.cons.fdprocedurecheck.FdProcedureCheckI18nConstant;
    import cloud.wconnect.workperformance.entity.fdprocedurecheck.FdProcedureCheck;
    import cloud.wconnect.workperformance.pojo.dto.fdprocedurecheck.ChangeFomClarificationDTO;
    import cloud.wconnect.workperformance.pojo.dto.fdprocedurecheck.FdProcedureCheckDTO;
    import cloud.wconnect.workperformance.pojo.dto.fdprocedurecheck.FdProcedureCheckSaveDTO;
    import cloud.wconnect.workperformance.pojo.group.General;
    import cloud.wconnect.workperformance.pojo.group.Other;
    import cloud.wconnect.workperformance.pojo.query.fdprocedurecheck.FdProcedureCheckQuery;
    import cloud.wconnect.workperformance.service.fdprocedurecheck.IFdProcedureCheckErrorTypeService;
    import cloud.wconnect.workperformance.service.fdprocedurecheck.IFdProcedureCheckProblemDescService;
    import cloud.wconnect.workperformance.service.fdprocedurecheck.IFdProcedureCheckService;
    import com.google.common.collect.Lists;
    import com.google.common.collect.Sets;
    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.validation.annotation.Validated;
    import org.springframework.web.bind.annotation.*;

    import java.io.IOException;
    import java.io.InputStream;
    import java.text.MessageFormat;
    import java.util.*;

    /**
     * <p>
     * FD Procedure Check 前端控制器
     * </p>
     *
     * @author zc
     * @since 2023-10-06
     */
    @RestController
    @RequestMapping("/fd-procedure-check")
    public class FdProcedureCheckController {

        @Resource
        private IFdProcedureCheckService fdProcedureCheckService;
        @Resource
        private IFdProcedureCheckErrorTypeService fdProcedureCheckErrorTypeService;
        @Resource
        private IFdProcedureCheckProblemDescService fdProcedureCheckProblemDescService;
        @Resource
        private PropertyI18nRedisService propertyI18nRedisService;
        @Resource
        private UserInternalService userInternalService;
        @Resource
        private SessionContext sessionContext;
        @Resource
        private I18nMessageUtils i18nMessageUtils;

        /**
         * 获取 FD Procedure Check
         *
         * @param property            资产标识
         * @param procedureCheckQuery 查询条件
         * @return FdProcedureCheckDTO 集合
         * @author zc
         * @date 2023/10/6
         */
        @ApiId(814)
        @GetMapping
        public ListPagedDTO<FdProcedureCheckDTO> list(String property, @Validated(General.class) FdProcedureCheckQuery procedureCheckQuery) {
            // 校验查询条件
            dateParamCheck(procedureCheckQuery.getFromDate(), procedureCheckQuery.getEndDate());
            typesParamCheck(procedureCheckQuery.getTypes());

            int tenantId = sessionContext.getTenantSymbolBO().getId();
            int propertyId = sessionContext.getPropertyId(property);
            return fdProcedureCheckService.list(tenantId, propertyId, procedureCheckQuery);
        }

        /**
         * 保存 FD Procedure Check
         *
         * @param property              资产标识
         * @param procedureCheckSaveDTO 保存内容
         * @return empty map
         * @author zc
         * @date 2023/10/6
         */
        @ApiId(815)
        @PostMapping
        public Map<Object, Object> save(String property, @RequestBody @Valid FdProcedureCheckSaveDTO procedureCheckSaveDTO) {
            int tenantId = sessionContext.getTenantSymbolBO().getId();
            int propertyId = sessionContext.getPropertyId(property);
            if (Objects.isNull(procedureCheckSaveDTO.getId())) {
                // 新增操作
                addFdProcedureCheck(tenantId, propertyId, procedureCheckSaveDTO);
            } else {
                // 编辑操作
                updateFdProcedureCheck(tenantId, propertyId, procedureCheckSaveDTO);
            }
            return Collections.emptyMap();
        }

        /**
         * 新增 FD Procedure Check
         *
         * @param tenantId              租户 id
         * @param propertyId            资产 id
         * @param procedureCheckSaveDTO 保存数据
         * @author zc
         * @date 2023/10/6
         */
        private void addFdProcedureCheck(int tenantId, int propertyId, FdProcedureCheckSaveDTO procedureCheckSaveDTO) {
            // 校验 FD Name User Id 有效性
            checkUserIdEffective(tenantId, propertyId, procedureCheckSaveDTO.getFdNameUserId());
            if (Objects.nonNull(procedureCheckSaveDTO.getFdReviewUserId())) {
                throw new BadRequestException(CommonErrorI18nConstant.MUST_EMPTY,
                        i18nMessageUtils.getErrorParamMapNotNeedTranslate("fdReviewUserId"),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The fdReviewUserId must be null.");
            }
            // 校验 Error Type
            fdProcedureCheckErrorTypeService.checkAttribute(propertyId, procedureCheckSaveDTO.getErrorTypeId(),
                    procedureCheckSaveDTO.getErrorTypeAdditional());
            // 校验 Problem Desc
            fdProcedureCheckProblemDescService.checkAttribute(propertyId, procedureCheckSaveDTO.getProblemDescId(),
                    procedureCheckSaveDTO.getProblemDescAdditional());

            // 新增数据
            fdProcedureCheckService.add(propertyId, sessionContext.getLoginUserId(), procedureCheckSaveDTO);
        }

        /**
         * 更新 FD Procedure Check
         *
         * @param tenantId              租户 id
         * @param propertyId            资产 id
         * @param procedureCheckSaveDTO 保存数据
         * @author zc
         * @date 2023/10/6
         */
        private void updateFdProcedureCheck(int tenantId, int propertyId, FdProcedureCheckSaveDTO procedureCheckSaveDTO) {
            // 校验数据是否存在
            FdProcedureCheck originData = fdProcedureCheckService.checkExist(procedureCheckSaveDTO.getId());
            // 校验数据归属
            fdProcedureCheckService.checkBelong(propertyId, originData);

            // 校验 FD Name User Id 有效性
            checkUserIdEffective(tenantId, propertyId, procedureCheckSaveDTO.getFdNameUserId());

            // 校验 Revise or Confirm 状态下，FD(after review) ReviewUserId 必传
            if (Lists.newArrayList(FdProcedureCheckHandledResultEnum.REVISE.getStatus(),
                    FdProcedureCheckHandledResultEnum.CONFIRM.getStatus()).contains(originData.getHandledResult())) {
                if (Objects.isNull(procedureCheckSaveDTO.getFdReviewUserId())) {
                    throw new BadRequestException(CommonErrorI18nConstant.CANNOT_EMPTY,
                            i18nMessageUtils.getErrorParamMapNotNeedTranslate("fdReviewUserId"),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            "The fdReviewUserId cannot be null.");
                }
            }

            // 校验 FD Review User Id 有效性
            checkUserIdEffective(tenantId, propertyId, procedureCheckSaveDTO.getFdReviewUserId());

            // 校验 Error Type
            fdProcedureCheckErrorTypeService.checkAttribute(propertyId, procedureCheckSaveDTO.getErrorTypeId(),
                    procedureCheckSaveDTO.getErrorTypeAdditional());
            // 校验 Problem Desc
            fdProcedureCheckProblemDescService.checkAttribute(propertyId, procedureCheckSaveDTO.getProblemDescId(),
                    procedureCheckSaveDTO.getProblemDescAdditional());

            // 更新数据
            fdProcedureCheckService.update(sessionContext.getLoginUserId(), procedureCheckSaveDTO);
        }

        /**
         * 删除 FD Procedure Check
         *
         * @param property 资产标识
         * @param id       FD Procedure Check id
         * @return empty map
         * @author zc
         * @date 2023/10/6
         */
        @ApiId(816)
        @DeleteMapping("/{id:\\d+}")
        public Map<Object, Object> delete(String property, @PathVariable long id) {
            int propertyId = sessionContext.getPropertyId(property);

            // 校验数据是否存在
            FdProcedureCheck originData = fdProcedureCheckService.checkExist(id);
            // 校验数据归属
            fdProcedureCheckService.checkBelong(propertyId, originData);
            // 删除数据
            fdProcedureCheckService.delete(id);

            return Collections.emptyMap();
        }

        /**
         * 确认 FD Procedure Check
         *
         * @param property 资产标识
         * @param id       FD Procedure Check id
         * @return empty map
         * @author zc
         * @date 2023/10/7
         */
        @ApiId(817)
        @PutMapping("/{id:\\d+}/confirm")
        public Map<Object, Object> confirm(String property, @PathVariable long id) {
            int propertyId = sessionContext.getPropertyId(property);

            // 校验数据是否存在
            FdProcedureCheck originData = fdProcedureCheckService.checkExist(id);
            // 校验数据归属
            fdProcedureCheckService.checkBelong(propertyId, originData);
            // 校验数据处理结果是否是 Revise
            if (!originData.getHandledResult().equals(FdProcedureCheckHandledResultEnum.REVISE.getStatus())) {
                throw new BadRequestException(FdProcedureCheckErrorI18nConstant.ONLY_CONFIRM_REVISE_STATUS,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "Only confirm handledResult is Revise data; handledResult: " + originData.getHandledResult());
            }
            // 确认数据
            fdProcedureCheckService.confirm(id, sessionContext.getLoginUserId());

            return Collections.emptyMap();
        }

        /**
         * 变更 FOM's clarification
         *
         * @param property                  资产标识
         * @param id                        FD Procedure Check id
         * @param changeFomClarificationDTO 内容
         * @return empty map
         * @author zc
         * @date 2023/10/7
         */
        @ApiId(818)
        @PutMapping("/{id:\\d+}/fom-clarification")
        public Map<Object, Object> changeFomClarification(String property, @PathVariable long id,
                                                          @RequestBody @Valid ChangeFomClarificationDTO changeFomClarificationDTO) {
            int propertyId = sessionContext.getPropertyId(property);

            // 校验数据是否存在
            FdProcedureCheck originData = fdProcedureCheckService.checkExist(id);
            // 校验数据归属
            fdProcedureCheckService.checkBelong(propertyId, originData);
            // 不能重复变更相同的状态
            if (originData.getHandledResult().equals(changeFomClarificationDTO.getHandledResult())) {
                throw new BadRequestException(FdProcedureCheckErrorI18nConstant.CANNOT_CHANGE_SAME_STATUS,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "Cannot change the data FOM's Clarification same status repeatedly");
            }
            // 校验数据处理结果是否是 Revise
            if (changeFomClarificationDTO.getHandledResult().equals(FdProcedureCheckHandledResultEnum.REVISE.getStatus())) {
                if (StringUtils.isBlank(changeFomClarificationDTO.getComment())) {
                    throw new BadRequestException(CommonErrorI18nConstant.CANNOT_EMPTY,
                            i18nMessageUtils.getErrorParamMapNeedTranslate(CommonFieldI18nConstant.COMMENT),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            "The comment cannot be blank.");
                }
            } else {
                if (StringUtils.isNotBlank(changeFomClarificationDTO.getComment())) {
                    throw new BadRequestException(CommonErrorI18nConstant.MUST_EMPTY,
                            i18nMessageUtils.getErrorParamMapNeedTranslate(CommonFieldI18nConstant.COMMENT),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            "The comment must be blank.");
                }
            }
            // 变更 FOM's Clarification
            fdProcedureCheckService.changeFomClarification(propertyId, id, changeFomClarificationDTO,
                    sessionContext.getLoginUserId());

            return Collections.emptyMap();
        }

        /**
         * 导出 FD Procedure Check
         *
         * @param property            资产标识
         * @param procedureCheckQuery 查询条件
         * @author zc
         * @date 2023/10/7
         */
        @ApiId(819)
        @GetMapping("/report")
        public void exportReport(String property, @Validated(Other.class) FdProcedureCheckQuery procedureCheckQuery,
                                 HttpServletResponse response) {
            // 校验查询条件
            dateParamCheck(procedureCheckQuery.getFromDate(), procedureCheckQuery.getEndDate());
            typesParamCheck(procedureCheckQuery.getTypes());

            try (InputStream is = new ClassPathResource("tmpl-xls/fd_procedure_check_tmpl_" + LocaleContextHolder.getLocale() + ".xlsx").getInputStream()) {
                int propertyId = sessionContext.getPropertyId(property);
                PropertyDTO propertyDTO = propertyI18nRedisService.get(propertyId);
                HashMap<String, Object> data = new HashMap<>();
                data.put("propertyName", propertyDTO.getName());
                data.put("list", fdProcedureCheckService.exportList(propertyId, procedureCheckQuery));

                String fileName = "FDProcedureCheck.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(is)
                        .buildAndFill(data, response::getOutputStream);
            } catch (IOException exp) {
                throw new InternalServerErrorException(BaseErrorCodeEnum.SYSTEM_ERROR.getI18nCode(),
                        BaseErrorCodeEnum.SYSTEM_ERROR.getCode(),
                        "Export FDProcedureCheck failed; exp: " + exp);
            }
        }

        /**
         * 校验日期查询条件
         *
         * @param fromDate 开始时间
         * @param endDate  结束时间
         * @author zc
         * @date 2023/10/6
         */
        private void dateParamCheck(Long fromDate, Long endDate) {
            if (Objects.isNull(fromDate) || Objects.isNull(endDate)) {
                return;
            }
            if (fromDate > endDate) {
                throw new BadRequestException(CommonErrorI18nConstant.START_DATE_CANNOT_GREATER_THAN_END_DATE,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        MessageFormat.format("The fromDate cannot be greater than endDate; fromDate:{0}, endDate:{1}",
                                fromDate, endDate));
            }
        }

        /**
         * 校验类型查询条件
         *
         * @param types 类型
         * @author zc
         * @date 2023/10/6
         */
        private void typesParamCheck(List<Integer> types) {
            if (!Sets.newHashSet(FdProcedureCheckQuery.listAllTypes()).containsAll(types)) {
                throw new BadRequestException(CommonErrorI18nConstant.MUST_IN_LIST,
                        i18nMessageUtils.getErrorParamMapNeedTranslate(CommonFieldI18nConstant.TYPE,
                                List.of(FdProcedureCheckI18nConstant.FD_NAME, FdProcedureCheckI18nConstant.FD_AFTER_REVIEW,
                                        FdProcedureCheckI18nConstant.FD_NAME_NOT_SURE, FdProcedureCheckI18nConstant.FD_AFTER_REVIEW_NA)),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The types must be in [1:FD Name; 2:FD(after review); 3:Not Sure; 4:N/A]; types: " + types);
            }
        }

        /**
         * 校验用户 id 有效性
         *
         * @param tenantId   租户 id
         * @param propertyId 资产 id
         * @param userId     用户 id
         * @author zc
         * @date 2023/10/6
         */
        private void checkUserIdEffective(int tenantId, int propertyId, Integer userId) {
            if (Objects.isNull(userId) || userId == BaseConstant.ZERO) {
                return;
            }
            userInternalService.checkUserIdEffective(tenantId, propertyId, userId);
        }
    }