cnas-process/src/main/java/com/ruoyi/process/service/impl/QualityMonitorServiceImpl.java
@@ -70,6 +70,9 @@
@Transactional(rollbackFor = Exception.class)
public class QualityMonitorServiceImpl extends ServiceImpl<QualityMonitorMapper, QualityMonitor> implements QualityMonitorService {
    private static final Set<String> EDITABLE_PLAN_STATES = new HashSet<>(Arrays.asList(
            "DRAFT", "REVIEW_REJECTED", "APPROVAL_REJECTED"));
    @Resource
    private QualityMonitorDetailsService qualityMonitorDetailsService;
    @Resource
@@ -96,6 +99,111 @@
    @Value("${wordUrl}")
    private String wordUrl;
    @Override
    public QualityMonitorDto createAnnualPlan(QualityMonitor input) {
        if (input == null || StringUtils.isBlank(input.getMonitorYear())
                || !input.getMonitorYear().matches("\\d{4}")) {
            throw new ErrorException("请选择正确的计划年度");
        }
        if (input.getExamineUserId() == null || userMapper.selectById(input.getExamineUserId()) == null) {
            throw new ErrorException("请选择有效的审核人");
        }
        if (StringUtils.isBlank(input.getMonitorName())) {
            throw new ErrorException("请填写计划名称");
        }
        int userId = SecurityUtils.getUserId().intValue();
        QualityMonitor plan = new QualityMonitor();
        plan.setMonitorYear(input.getMonitorYear());
        plan.setMonitorName(input.getMonitorName().trim());
        plan.setWriteUserId(userId);
        plan.setWriteTime(LocalDateTime.now());
        plan.setExamineUserId(input.getExamineUserId());
        plan.setWorkflowStatus("DRAFT");
        baseMapper.insert(plan);
        return baseMapper.selectPlanDtoById(plan.getQualityMonitorId());
    }
    @Override
    public QualityMonitorDto getAnnualPlan(String monitorYear, Integer qualityMonitorId) {
        QualityMonitor plan;
        if (qualityMonitorId != null) {
            plan = baseMapper.selectById(qualityMonitorId);
            if (plan == null || (StringUtils.isNotBlank(monitorYear)
                    && !monitorYear.equals(plan.getMonitorYear()))) return null;
        } else {
            if (StringUtils.isBlank(monitorYear)) throw new ErrorException("请选择计划年度");
            plan = baseMapper.selectOne(Wrappers.<QualityMonitor>lambdaQuery()
                    .eq(QualityMonitor::getMonitorYear, monitorYear)
                    .orderByDesc(QualityMonitor::getCreateTime, QualityMonitor::getQualityMonitorId)
                    .last("limit 1"));
        }
        return plan == null ? null : baseMapper.selectPlanDtoById(plan.getQualityMonitorId());
    }
    @Override
    public boolean submitAnnualPlan(Integer qualityMonitorId) {
        QualityMonitor plan = requirePlan(qualityMonitorId);
        int current = SecurityUtils.getUserId().intValue();
        if (!Integer.valueOf(current).equals(plan.getWriteUserId())) {
            throw new ErrorException("只有计划编制人可以提交审核");
        }
        String state = effectiveStatus(plan);
        if (!EDITABLE_PLAN_STATES.contains(state)) throw new ErrorException("当前计划状态不能提交审核");
        if (qualityMonitorDetailsMapper.selectCount(Wrappers.<QualityMonitorDetails>lambdaQuery()
                .eq(QualityMonitorDetails::getQualityMonitorId, qualityMonitorId)) == 0) {
            throw new ErrorException("请至少新增一条质控项目后再提交审核");
        }
        baseMapper.update(null, Wrappers.<QualityMonitor>lambdaUpdate()
                .eq(QualityMonitor::getQualityMonitorId, qualityMonitorId)
                .set(QualityMonitor::getWorkflowStatus, "PENDING_REVIEW")
                .set(QualityMonitor::getExamineStatus, null)
                .set(QualityMonitor::getExamineRemark, null)
                .set(QualityMonitor::getRatifyStatus, null));
        sendPlanNotice(plan, plan.getExamineUserId(), "CNAS质量监控计划审核通知", "质量监控计划待审核");
        return true;
    }
    @Override
    public void validateDetailEditable(Integer qualityMonitorId) {
        QualityMonitor plan = requirePlan(qualityMonitorId);
        if (!Integer.valueOf(SecurityUtils.getUserId().intValue()).equals(plan.getWriteUserId())) {
            throw new ErrorException("只有计划编制人可以维护质控项目");
        }
        if (!EDITABLE_PLAN_STATES.contains(effectiveStatus(plan))) {
            throw new ErrorException("当前计划已提交,不能修改质控项目");
        }
    }
    private QualityMonitor requirePlan(Integer id) {
        QualityMonitor plan = id == null ? null : baseMapper.selectById(id);
        if (plan == null) throw new ErrorException("年度质量控制计划不存在");
        return plan;
    }
    private String effectiveStatus(QualityMonitor plan) {
        if (StringUtils.isNotBlank(plan.getWorkflowStatus())) return plan.getWorkflowStatus();
        if (Integer.valueOf(1).equals(plan.getRatifyStatus())) return "APPROVED";
        if (Integer.valueOf(0).equals(plan.getRatifyStatus())) return "APPROVAL_REJECTED";
        if (Integer.valueOf(1).equals(plan.getExamineStatus())) return "PENDING_APPROVAL";
        if (Integer.valueOf(0).equals(plan.getExamineStatus())) return "REVIEW_REJECTED";
        return "DRAFT";
    }
    private void sendPlanNotice(QualityMonitor plan, Integer receiverId, String theme, String suffix) {
        if (receiverId == null) return;
        int sender = SecurityUtils.getUserId().intValue();
        User senderUser = userMapper.selectById(sender);
        InformationNotification info = new InformationNotification();
        info.setCreateUser(senderUser == null ? "" : senderUser.getName());
        info.setMessageType("6");
        info.setTheme(theme);
        info.setContent(plan.getMonitorName() + suffix);
        info.setSenderId(sender);
        info.setConsigneeId(receiverId);
        info.setJumpPath(MenuJumpPathConstants.QUALITY_MONITOR);
        informationNotificationService.addInformationNotification(info);
    }
    /**
     * 导入监控计划
@@ -108,17 +216,18 @@
        Integer userId = SecurityUtils.getUserId().intValue();
        // 文件名称
        String fileName = file.getOriginalFilename().substring(0, file.getOriginalFilename().lastIndexOf("."));
        if (monitor.getMonitorYear() == null || !monitor.getMonitorYear().matches("\\d{4}")) {
            throw new ErrorException("请选择正确的计划年度");
        }
        if (monitor.getExamineUserId() == null) throw new ErrorException("缺少审核人");
        QualityMonitor qualityMonitor = new QualityMonitor();
        qualityMonitor.setMonitorName(fileName);
        qualityMonitor.setMonitorYear(monitor.getMonitorYear());
        qualityMonitor.setWriteUserId(userId);
        qualityMonitor.setExamineUserId(monitor.getExamineUserId());
        qualityMonitor.setWriteTime(LocalDateTime.now());
        qualityMonitor.setWorkflowStatus("PENDING_REVIEW");
        baseMapper.insert(qualityMonitor);
        if (monitor.getExamineUserId() == null) {
            throw new ErrorException("缺少审核人");
        }
        User user = userMapper.selectById(userId);
        // 消息发送
@@ -159,14 +268,27 @@
            EasyExcel.read(file.getInputStream(), QualityMonitorDetailsUpload.class, new AnalysisEventListener<QualityMonitorDetailsUpload>() {
                @Override
                public void invoke(QualityMonitorDetailsUpload detailsUpload, AnalysisContext analysisContext) {
                    // 判断是否为空
                    if (StringUtils.isBlank(detailsUpload.getPlannedTime())) {
                        throw new ErrorException("计划开展时间不能为空");
                    if (StringUtils.isBlank(detailsUpload.getMonitorProject())
                            || StringUtils.isBlank(detailsUpload.getTechnicalStandardText())
                            || StringUtils.isBlank(detailsUpload.getMonitorWay())
                            || StringUtils.isBlank(detailsUpload.getCompletionMonth())) {
                        throw new ErrorException("质控项目、技术标准、质控方式和完成时限不能为空");
                    }
                        // 对象复制
                    QualityMonitorDetails monitorDetails = new QualityMonitorDetails();
                    BeanUtils.copyProperties(detailsUpload, monitorDetails);
                    monitorDetails.setQualityMonitorId(qualityMonitor.getQualityMonitorId());
                    if (!detailsUpload.getCompletionMonth().matches("\\d{4}-\\d{2}")) {
                        throw new ErrorException("完成时限格式应为yyyy-MM");
                    }
                    if (StringUtils.isBlank(detailsUpload.getResponsibleName())) {
                        throw new ErrorException("负责人不能为空");
                    }
                    List<User> users = userMapper.selectList(Wrappers.<User>lambdaQuery()
                            .eq(User::getName, detailsUpload.getResponsibleName()));
                    if (users.size() != 1) throw new ErrorException("负责人姓名不存在或不唯一:" + detailsUpload.getResponsibleName());
                    monitorDetails.setResponsibleUserId(users.get(0).getId());
                    monitorDetails.setItemKind("OTHER");
                    detailsUploadList.add(monitorDetails);
                }
@@ -191,8 +313,16 @@
     */
    @Override
    public boolean examineQualityMonitor(QualityMonitor qualityMonitor) {
        if (qualityMonitor.getRatifyUserId() == null) {
            throw new ErrorException("缺少批准人");
        QualityMonitor stored = requirePlan(qualityMonitor.getQualityMonitorId());
        int current = SecurityUtils.getUserId().intValue();
        if (!Integer.valueOf(current).equals(stored.getExamineUserId())) {
            throw new ErrorException("只有指定审核人可以审核计划");
        }
        if (!"PENDING_REVIEW".equals(effectiveStatus(stored))) {
            throw new ErrorException("当前计划不在待审核状态");
        }
        if (Integer.valueOf(1).equals(qualityMonitor.getExamineStatus()) && qualityMonitor.getRatifyUserId() == null) {
            throw new ErrorException("审核通过时必须指定批准人");
        }
        // 当前登录用户
@@ -202,7 +332,11 @@
                .set(QualityMonitor::getExamineRemark, qualityMonitor.getExamineRemark())
                .set(QualityMonitor::getExamineStatus, qualityMonitor.getExamineStatus())
                .set(QualityMonitor::getExamineTime, LocalDateTime.now())
                .set(QualityMonitor::getWorkflowStatus,
                        Integer.valueOf(1).equals(qualityMonitor.getExamineStatus()) ? "PENDING_APPROVAL" : "REVIEW_REJECTED")
        );
        if (!Integer.valueOf(1).equals(qualityMonitor.getExamineStatus())) return true;
        QualityMonitor monitor = baseMapper.selectById(qualityMonitor.getQualityMonitorId());
@@ -247,12 +381,22 @@
     */
    @Override
    public boolean ratifyQualityMonitor(QualityMonitor qualityMonitor) {
        QualityMonitor stored = requirePlan(qualityMonitor.getQualityMonitorId());
        int current = SecurityUtils.getUserId().intValue();
        if (!Integer.valueOf(current).equals(stored.getRatifyUserId())) {
            throw new ErrorException("只有指定批准人可以批准计划");
        }
        if (!"PENDING_APPROVAL".equals(effectiveStatus(stored))) {
            throw new ErrorException("当前计划不在待批准状态");
        }
        // 当前登录用户
        baseMapper.update(null, Wrappers.<QualityMonitor>lambdaUpdate()
                .eq(QualityMonitor::getQualityMonitorId, qualityMonitor.getQualityMonitorId())
                .set(QualityMonitor::getRatifyRemark, qualityMonitor.getRatifyRemark())
                .set(QualityMonitor::getRatifyStatus, qualityMonitor.getRatifyStatus())
                .set(QualityMonitor::getRatifyTime, LocalDateTime.now())
                .set(QualityMonitor::getWorkflowStatus,
                        Integer.valueOf(1).equals(qualityMonitor.getRatifyStatus()) ? "APPROVED" : "APPROVAL_REJECTED")
        );
        return true;
    }