| | |
| | | package com.ruoyi.process.service.impl; |
| | | |
| | | import cn.hutool.core.bean.BeanUtil; |
| | | import cn.hutool.core.bean.BeanUtil; |
| | | import cn.hutool.core.util.StrUtil; |
| | | import cn.hutool.json.JSONArray; |
| | | import cn.hutool.json.JSONObject; |
| | | import cn.hutool.json.JSONUtil; |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; |
| | | import com.baomidou.mybatisplus.core.toolkit.StringUtils; |
| | |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | import org.apache.poi.xwpf.usermodel.XWPFDocument; |
| | | import org.apache.poi.xwpf.usermodel.XWPFParagraph; |
| | | import org.apache.poi.xwpf.usermodel.XWPFRun; |
| | | import org.apache.poi.xwpf.usermodel.XWPFTable; |
| | | import org.apache.poi.xwpf.usermodel.XWPFTableCell; |
| | | |
| | | import jakarta.annotation.Resource; |
| | | import jakarta.servlet.http.HttpServletResponse; |
| | |
| | | import java.util.ArrayList; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.LinkedHashSet; |
| | | import java.util.Objects; |
| | | import java.util.Set; |
| | | |
| | | /** |
| | | * 标准方法验证 |
| | |
| | | if (methodVerifyDto.getOperationType() == null) { |
| | | throw new ErrorException("请选择是变更还是验证"); |
| | | } |
| | | validateWordVerification(methodVerifyDto); |
| | | // 新增 |
| | | baseMapper.insert(methodVerifyDto); |
| | | |
| | |
| | | return processMethodVerifyDto; |
| | | } |
| | | |
| | | @Override |
| | | public List<ProcessMethodVerifyWorkFile> getCurrentWorkFiles(String userIds) { |
| | | List<ProcessMethodVerifyWorkFile> files = new ArrayList<>(); |
| | | if (StringUtils.isBlank(userIds)) return files; |
| | | |
| | | Set<Integer> ids = new LinkedHashSet<>(); |
| | | for (String value : StrUtil.split(userIds, ",")) { |
| | | try { |
| | | ids.add(Integer.valueOf(value.trim())); |
| | | } catch (NumberFormatException e) { |
| | | throw new ErrorException("检测人员ID无效"); |
| | | } |
| | | } |
| | | if (ids.size() > 5) throw new ErrorException("最多选择5名检测人员"); |
| | | |
| | | for (Integer userId : ids) { |
| | | ProcessMethodVerifyWorkFile file = processMethodVerifyWorkFileMapper.selectLatestAuthorization(userId); |
| | | if (file == null) continue; |
| | | file.setUserId(userId); |
| | | if (StringUtils.isBlank(file.getFileUrl())) { |
| | | file.setFileName("未上传上岗证书"); |
| | | file.setType(0); |
| | | } else { |
| | | file.setFileName(file.getFileUrl()); |
| | | file.setType(file.getFileUrl().matches("(?i).*\\.(png|jpe?g|gif|bmp|webp)$") ? 1 : 2); |
| | | } |
| | | files.add(file); |
| | | } |
| | | return files; |
| | | } |
| | | |
| | | /** |
| | | * 修改标准方法验证 |
| | | * |
| | |
| | | */ |
| | | @Override |
| | | public boolean updateMethodVerify(ProcessMethodVerifyDto methodVerifyDto) { |
| | | validateWordVerification(methodVerifyDto); |
| | | baseMapper.updateById(methodVerifyDto); |
| | | |
| | | // 删除上岗证 |
| | |
| | | public void exportMethodVerify(Integer methodVerifyId, HttpServletResponse response) { |
| | | // 获取标准方法更新验证数据 |
| | | ProcessMethodVerify processMethodVerify = baseMapper.selectById(methodVerifyId); |
| | | if (processMethodVerify == null) { |
| | | throw new ErrorException("方法验证记录不存在"); |
| | | } |
| | | if (Objects.equals(processMethodVerify.getOperationType(), 1) |
| | | && StringUtils.isNotBlank(processMethodVerify.getVerificationContent())) { |
| | | exportWordVerification(processMethodVerify, response); |
| | | return; |
| | | } |
| | | |
| | | // 将内部字段转换成展示到word中的对象 |
| | | ProcessMethodVerifyExportWordDto exportWordTemplate = getExportWordTemplate(processMethodVerify); |
| | |
| | | } |
| | | } |
| | | |
| | | /** 按客户提供的横向方法验证表导出;原始记录和验证结果仍作为附件保存。 */ |
| | | private void exportWordVerification(ProcessMethodVerify verify, HttpServletResponse response) { |
| | | JSONObject content = JSONUtil.parseObj(verify.getVerificationContent()); |
| | | String[] checkKeys = {"personnel", "equipment", "reagents", "conditions", "sample", "steps", |
| | | "processing", "qualityControl", "result"}; |
| | | InputStream templateStream = getClass().getResourceAsStream("/static/method-verification-customer.docx"); |
| | | if (templateStream == null) throw new IllegalStateException("方法验证导出模板不存在"); |
| | | try (InputStream inputStream = templateStream; XWPFDocument document = new XWPFDocument(inputStream)) { |
| | | XWPFTable table = document.getTables().get(0); |
| | | setTemplateCell(table, 0, 1, verify.getStandardName()); |
| | | setTemplateCell(table, 0, 3, verify.getStandardCode()); |
| | | setTemplateCell(table, 1, 1, verify.getVerifyItem()); |
| | | |
| | | JSONArray entries = content.getJSONArray("checks"); |
| | | for (int i = 0; i < checkKeys.length; i++) { |
| | | JSONObject entry = findCheck(entries, checkKeys[i]); |
| | | Integer satisfied = entry == null ? null : entry.getInt("satisfied"); |
| | | int row = i + 3; |
| | | if (row <= 8) { |
| | | setTemplateCell(table, row, 1, field(entry, "requirement")); |
| | | setTemplateCell(table, row, 2, field(entry, "capability")); |
| | | setTemplateCell(table, row, 3, satisfiedText(satisfied)); |
| | | } else { |
| | | setTemplateCell(table, row, 1, mergedCheckText(entry)); |
| | | setTemplateCell(table, row, 2, satisfiedText(satisfied)); |
| | | } |
| | | } |
| | | setTemplateCell(table, 12, 1, field(content, "conclusion")); |
| | | setTemplateCell(table, 13, 1, decisionText(field(content, "reviewDecision"), |
| | | "评审人员", field(content, "reviewer"), field(content, "reviewDate"))); |
| | | setTemplateCell(table, 14, 1, decisionText(field(content, "approvalDecision"), |
| | | "技术负责人", field(content, "approver"), field(content, "approvalDate"))); |
| | | |
| | | response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document"); |
| | | String fileName = URLEncoder.encode("方法验证记录", "UTF-8"); |
| | | response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".docx"); |
| | | document.write(response.getOutputStream()); |
| | | } catch (Exception e) { |
| | | throw new RuntimeException("导出方法验证记录失败", e); |
| | | } |
| | | } |
| | | |
| | | private JSONObject findCheck(JSONArray entries, String key) { |
| | | if (entries == null) return null; |
| | | for (Object value : entries) { |
| | | JSONObject check = JSONUtil.parseObj(value); |
| | | if (key.equals(check.getStr("key"))) return check; |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private String field(JSONObject object, String key) { |
| | | return object == null ? "" : StrUtil.nullToEmpty(object.getStr(key)); |
| | | } |
| | | |
| | | private String satisfiedText(Integer satisfied) { |
| | | return satisfied == null ? "" : satisfied == 1 ? "是" : "否"; |
| | | } |
| | | |
| | | private String mergedCheckText(JSONObject entry) { |
| | | String requirement = field(entry, "requirement").trim(); |
| | | String capability = field(entry, "capability").trim(); |
| | | if (requirement.isEmpty()) return capability; |
| | | if (capability.isEmpty() || requirement.equals(capability)) return requirement; |
| | | return "方法要求:" + requirement + "\n实验室能力:" + capability; |
| | | } |
| | | |
| | | private String decisionText(String decision, String personLabel, String person, String date) { |
| | | StringBuilder value = new StringBuilder(); |
| | | for (String choice : new String[]{"同意使用", "不能使用", "限制使用"}) { |
| | | if (value.length() > 0) value.append(" "); |
| | | value.append(choice.equals(decision) ? "☑" : "□").append(choice); |
| | | } |
| | | value.append("\n").append(personLabel).append(":").append(person); |
| | | if (StringUtils.isNotBlank(date)) { |
| | | String[] parts = date.split("-"); |
| | | value.append(" 日期:"); |
| | | value.append(parts.length == 3 ? parts[0] + "年" + parts[1] + "月" + parts[2] + "日" : date); |
| | | } |
| | | return value.toString(); |
| | | } |
| | | |
| | | private void setTemplateCell(XWPFTable table, int row, int cellIndex, String value) { |
| | | XWPFTableCell cell = table.getRow(row).getCell(cellIndex); |
| | | XWPFParagraph paragraph = cell.getParagraphs().get(0); |
| | | XWPFRun run = paragraph.getRuns().get(0); |
| | | String[] lines = StrUtil.nullToEmpty(value).replace("\r\n", "\n").split("\n", -1); |
| | | run.setText(lines[0], 0); |
| | | for (int i = 1; i < lines.length; i++) { |
| | | run.addBreak(); |
| | | run.setText(lines[i]); |
| | | } |
| | | } |
| | | |
| | | private void validateWordVerification(ProcessMethodVerifyDto verify) { |
| | | // 没有 verificationContent 的旧记录继续由旧表单维护。 |
| | | if (!Objects.equals(verify.getOperationType(), 1) |
| | | || StringUtils.isBlank(verify.getVerificationContent())) return; |
| | | if (StringUtils.isBlank(verify.getStandardName()) |
| | | || StringUtils.isBlank(verify.getStandardCode()) |
| | | || StringUtils.isBlank(verify.getVerifyItem())) { |
| | | throw new ErrorException("请填写标准名称、标准编号和验证项目"); |
| | | } |
| | | try { |
| | | JSONUtil.parseObj(verify.getVerificationContent()); |
| | | } catch (Exception e) { |
| | | throw new ErrorException("方法验证内容格式错误"); |
| | | } |
| | | // 保留旧列供现有依赖 method_name 的查询使用。 |
| | | verify.setMethodName(verify.getStandardName()); |
| | | } |
| | | |
| | | private ProcessMethodVerifyExportWordDto getExportWordTemplate(ProcessMethodVerify processMethodVerify) { |
| | | ProcessMethodVerifyExportWordDto processMethodVerifyExportWordDto = new ProcessMethodVerifyExportWordDto(); |
| | | BeanUtil.copyProperties(processMethodVerify, processMethodVerifyExportWordDto); |
| | |
| | | } |
| | | // 确认日期 |
| | | processMethodVerifyExportWordDto.setConfirmDateStr(processMethodVerify.getConfirmDate() != null ? processMethodVerify.getConfirmDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")): null); |
| | | // 评审意见 |
| | | processMethodVerifyExportWordDto.setReviewResultStr(buildOpinionCheckBox(processMethodVerify.getReviewResult())); |
| | | processMethodVerifyExportWordDto.setReviewDateStr(processMethodVerify.getReviewDate() != null ? processMethodVerify.getReviewDate().format(DateTimeFormatter.ofPattern("yyyy年MM月dd日")): null); |
| | | // 审批意见 |
| | | processMethodVerifyExportWordDto.setApprovalResultStr(buildOpinionCheckBox(processMethodVerify.getApprovalResult())); |
| | | processMethodVerifyExportWordDto.setApprovalDateStr(processMethodVerify.getApprovalDate() != null ? processMethodVerify.getApprovalDate().format(DateTimeFormatter.ofPattern("yyyy年MM月dd日")): null); |
| | | return processMethodVerifyExportWordDto; |
| | | } |
| | | |
| | | /** |
| | | * 拼接"同意使用/不能使用/限制使用"勾选项展示串 |
| | | */ |
| | | private String buildOpinionCheckBox(String result) { |
| | | return ("同意使用".equals(result) ? "☑" : "□") + "同意使用 " |
| | | + ("不能使用".equals(result) ? "☑" : "□") + "不能使用 " |
| | | + ("限制使用".equals(result) ? "☑" : "□") + "限制使用"; |
| | | } |
| | | |
| | | |
| | |
| | | User user = userMapper.selectById(userId); |
| | | ProcessMethodVerifyWorkFile workFile = new ProcessMethodVerifyWorkFile(); |
| | | workFile.setMethodVerifyId(methodVerifyDto.getMethodVerifyId()); |
| | | workFile.setType(1); |
| | | workFile.setType(workName.matches("(?i).*\\.(png|jpe?g|gif|bmp|webp)$") ? 1 : 2); |
| | | workFile.setFileUrl(workName); |
| | | workFile.setFileName(workName); |
| | | workFile.setUserId(user.getId()); |