fix 测试用例导入兼容格式问题;新增部分用例导入接口

This commit is contained in:
曾威嶂 2025-07-02 10:39:49 +08:00
parent 3b17e0c729
commit 8045096c02
3 changed files with 267 additions and 37 deletions

View File

@ -144,12 +144,20 @@ public class PmsProjectTestcaseController extends BaseController {
@ApiOperation(value = "批量下载测试用例")
@GetMapping("/{projectId}/download")
public void download(@PathVariable("projectId") Long projectId, HttpServletResponse response)throws IOException {
PmsProjectTestcaseModuleSearchVo pmsProjectTestcaseModuleSearchVo = new PmsProjectTestcaseModuleSearchVo();
pmsProjectTestcaseModuleSearchVo.setPmsProjectId(projectId);
List<TreeSelect> treeSelects = pmsProductModuleService.getPmsProductModuleTreeList(projectId, PmsConstants.TESTCASE_TAG_AND_MODULE_TYPE);
pmsProjectTestcaseService.generateWord(treeSelects, projectId, response);
}
/**
* 部分下载测试用例
*/
@ApiOperation(value = "选择部分下载测试用例")
@GetMapping("/{projectId}/partDownload")
public void partDownload(@PathVariable Long projectId, @RequestParam String ids, HttpServletResponse response)throws IOException {
List<TreeSelect> treeSelects = pmsProductModuleService.getPmsProductModuleTreeList(projectId, PmsConstants.TESTCASE_TAG_AND_MODULE_TYPE);
pmsProjectTestcaseService.generateWordByTestcaseId(treeSelects, projectId, ids, response);
}
/**
* 批量导入测试用例
*/

View File

@ -139,4 +139,15 @@ public interface IPmsProjectTestcaseService {
* @param file 导入的docx文件
*/
void testcaseImport(Long projectId, MultipartFile file) throws IOException;
/**
* 获取测试用例列表
*
* @param treeSelects 模块树结构
* @param projectId 项目id
* @param ids 选择测试用例id
* @param response
* @return 导出word数据流
*/
void generateWordByTestcaseId(List<TreeSelect> treeSelects, Long projectId, String ids, HttpServletResponse response) throws IOException;
}

View File

@ -153,7 +153,7 @@ public class PmsProjectTestcaseServiceImpl implements IPmsProjectTestcaseService
, x -> pmsProductModuleService.selectPmsProductModuleById(x, false)
, pmsProjectTestcase.getPmsModuleId()));
}
if (pmsProjectTestcase.getIsImport() != null) {
if (pmsProjectTestcase.getIsImport() == null) {
// 设置需求创建人
pmsProjectTestcaseDataVo.setCreateBy(
PmsUtils.getSelectObjectInCache(
@ -1109,19 +1109,25 @@ public class PmsProjectTestcaseServiceImpl implements IPmsProjectTestcaseService
run.setText(text);
}
// 插入分页符另起一页
// 插入表格
private int insertTable(XWPFDocument doc,List<PendingOutputDataVo> pendingOutputDatas, int counts) {
for (int i = 0; i < pendingOutputDatas.size(); i++) {
counts = counts + 1;
PendingOutputDataVo pendingOutputData = pendingOutputDatas.get(i);
//addNumberedTableTitle(doc, pendingOutputData.getTitle());
addNumberedTableTitle(doc, pendingOutputData.getTitle(), counts);
creatTableTemplate(doc,pendingOutputData);
}
return counts;
}
XWPFParagraph pageBreak = doc.createParagraph();
XWPFRun pageBreakRun = pageBreak.createRun();
pageBreakRun.addBreak(BreakType.PAGE);
private int insertTable(XWPFDocument doc,List<PendingOutputDataVo> pendingOutputDatas, int counts, List<Long> idList) {
for (int i = 0; i < pendingOutputDatas.size(); i++) {
PendingOutputDataVo pendingOutputData = pendingOutputDatas.get(i);
if (idList.contains(pendingOutputData.getId())) {
counts = counts + 1;
addNumberedTableTitle(doc, pendingOutputData.getTitle(), counts);
creatTableTemplate(doc,pendingOutputData);
}
}
return counts;
}
@ -1238,6 +1244,99 @@ public class PmsProjectTestcaseServiceImpl implements IPmsProjectTestcaseService
}
private void fillFixedContent(XWPFDocument document, Long projectId, List<TreeSelect> treeSelects, List<Long> idList){
String mainText = "格式说明: 正文宋体、五号、段落间距固定值20磅各级标题黑体、五号、段落间距固定值20磅";
fillMainContent(document,mainText);
// 写入第一章内容
addNumberedHeading(document,"范围",1, false);
// 1.1
addNumberedHeading(document,"标识",2, false);
mainText = "本条应描述本文档所适用系统和软件的完整标识,(若适用)包括其标识号、名称、缩略名、版本号和发布号。";
fillMainContent(document,mainText);
//1.2
addNumberedHeading(document,"系统概述",2, false);
mainText = "本条应概述本文档适用的系统和软件的用途;描述系统和软件的一般特性(如规模、安全性、可靠性、实时性、技术风险等特性)"+
"概述系统开发、运行和维护的历史;标识项目的需方、用户、开发方和保障机构等;标识当前和计划的运行现场;列出其他有关文档。";
fillMainContent(document,mainText);
//1.3
addNumberedHeading(document,"文档概述",2, false);
mainText = "本条应概述本文档的用途和内容,并描述与它的使用有关的安全保密方面的要求。";
fillMainContent(document,mainText);
// 写入第二章内容
addNumberedHeading(document,"引用文档",1, false);
mainText = "本章应列出引用文档的编号、标题、编写单位、修订版及日期,还应给出不能通过正常渠道得到的文档的来源。";
fillMainContent(document,mainText);
// 写入第三章内容
addNumberedHeading(document,"测试准备",1, false);
mainText = "本章应分为以下子条。(若适用)应包括用“警告”或“注意”所标志的安全提示,以及保密性考虑。";
fillMainContent(document,mainText);
// 3.1
addNumberedHeading(document,"(测试唯一标识符)",2, false);
mainText = "本条应使用项目的唯一标识符来标识一个测试,并对测试进行简要说明,同时应分为以下子条描述。" +
"若与另一个测试所要求的信息存在重复时,可直接引用。";
fillMainContent(document,mainText);
// 3.1.1
addNumberedHeading(document,"硬件准备",3, false);
mainText = "本条应描述测试工作所需的硬件准备规程。有关这些规程,可以引用已发布的操作手册。(若适用)应提供以下内容:";
fillMainContent(document,mainText);
mainText = "a) 用名称和(若适用)编号标识要使用的特定硬件;";
fillMainContent(document,mainText);
mainText = "b) 所有连接硬件所需的开关装置和电缆;";
fillMainContent(document,mainText);
mainText = "c) 说明硬件、互联控制和数据路径的一个或多个图示;";
fillMainContent(document,mainText);
mainText = "d) 使硬件处于就绪状态的逐步的操作说明;";
fillMainContent(document,mainText);
//3.1.2
addNumberedHeading(document,"软件准备",3, false);
mainText = "本条应描述准备被测项、相关软件以及数据的必要规程。有关这些规程,可以引用已发布的软件手册。(若适用)应提供下述信息:";
fillMainContent(document,mainText);
mainText = "a) 测试中要使用的特定软件;";
fillMainContent(document,mainText);
mainText = "b) 被测项的存储介质(如光盘、磁盘)";
fillMainContent(document,mainText);
mainText = "c) 所有相关软件(如模拟器、测试驱动程序、数据库)的存储介质;";
fillMainContent(document,mainText);
mainText = "d) 加载软件的说明,包括所需的顺序;";
fillMainContent(document,mainText);
mainText = "e) 多个测试用例共用的软件初始化说明;";
fillMainContent(document,mainText);
//3.1.3
addNumberedHeading(document,"其它测试前准备",3, false);
mainText = "本条应描述其他测试前所需的人员活动、准备工作或规程。";
fillMainContent(document,mainText);
// 写入第四章内容
addNumberedHeading(document,"测试说明",1, false);
mainText = "本章应分为以下子条。(若适用)应包括用“警告”或“注意”所标志的安全提示,以及保密性考虑。";
fillMainContent(document,mainText);
// 添加带自动编号的内容
iterateTraversl(projectId, treeSelects, document, idList);
//写入第五章内容
addNumberedHeading(document,"需求的可追踪性",1, false);
mainText = "本条应描述:";
fillMainContent(document,mainText);
mainText = "a) 从软件测试说明中的测试用例到它所涉及的系统或CSCI需求的可追踪性。" +
"若一个测试用例涉及多个需求,应包含从每一组测试规程步骤到所涉及的需求的可追踪性(亦可在4.X.Y.1中提供)。";
fillMainContent(document,mainText);
mainText = "b) 从本软件测试说明所提及的每项系统或CSCI需求到涉及它们的测试用例的可追踪性。" +
"对于CSCI测试是从CSCI的软件需求规格说明(SRS)和有关接口需求规格说明(IRS)中的每项CSCI需求到涉及它们的测试用例的可追踪性。" +
"对于系统测试,是从系统的系统/子系统规格说明(SSS)及有关IRS中的每项系统需求到涉及它们的测试用例的可追踪性。" +
"如果一个测试用例涉及多项需求,则可追踪性应指明涉及每项需求的特定测试规程步骤。";
fillMainContent(document,mainText);
mainText = "软件测试说明可通过软件测试计划建立与系统或CSCI需求双向可追踪性";
fillMainContent(document,mainText);
//写入第六章内容
addNumberedHeading(document,"注释",1, false);
mainText = "本章应包括有助于了解文档的所有信息(例如:背景、术语、缩略语或公式)。";
fillMainContent(document,mainText);
}
private void iterateTraversl(Long projectId, List<TreeSelect> treeSelects, XWPFDocument document){
// 使用栈进行迭代遍历
@ -1282,6 +1381,49 @@ public class PmsProjectTestcaseServiceImpl implements IPmsProjectTestcaseService
}
}
private void iterateTraversl(Long projectId, List<TreeSelect> treeSelects, XWPFDocument document, List<Long> idList){
// 使用栈进行迭代遍历
Stack<TreeSelect> stack = new Stack<>();
// 先将所有根节点压入栈
if (treeSelects != null) {
for (int i = treeSelects.size() - 1; i >= 0; i--) {
stack.push(treeSelects.get(i));
}
}
int tableCounts = 0;
while (!stack.isEmpty()) {
TreeSelect current = stack.pop();
// 处理当前节点这里简单打印节点信息你可以根据需求修改
//System.out.println("ID: " + current.getId() + ", 名称: " + current.getLabel() + ", 层级:" + current.getLevel() + ", 父ID: " + current.getParentId() + ", 是否叶子节点: " + current.getIsLeaf() );
if (current.getIsLeaf()) {
if(current.getId() > 0){
addNumberedHeading(document, current.getLabel(), current.getLevel() + 1, true);
List<PendingOutputDataVo> pendingOutputDatas = GetPendingOutputData(projectId, current.getId());
tableCounts = insertTable(document, pendingOutputDatas, tableCounts, idList);
} else {
addNumberedHeading(document, current.getLabel(), current.getLevel() + 1, true);
List<PendingOutputDataVo> pendingOutputDatas = GetPendingOutputData(projectId, null);
tableCounts = insertTable(document, pendingOutputDatas, tableCounts, idList);
}
} else{
if (current.getId() > 0){
addNumberedHeading(document, current.getLabel(), current.getLevel() + 1, true);
}
}
//处理当前节点结束
// 将子节点压入栈
List<TreeSelect> children = current.getChildren();
if (children != null) {
for (int i = children.size() - 1; i >= 0; i--) {
stack.push(children.get(i));
}
}
}
}
public void generateWord(List<TreeSelect> treeSelects, Long projectId, HttpServletResponse response) throws IOException {
// 创建一个新的 Word 文档
XWPFDocument document = new XWPFDocument();
@ -1316,6 +1458,43 @@ public class PmsProjectTestcaseServiceImpl implements IPmsProjectTestcaseService
document.close();
}
@Override
public void generateWordByTestcaseId(List<TreeSelect> treeSelects, Long projectId, String ids, HttpServletResponse response) throws IOException{
List<Long> idList = Arrays.stream(ids.split(",")).map(String::trim).map(Long::parseLong).collect(Collectors.toList());
checkBatchUpdateInTheSameProject(idList);
// 创建一个新的 Word 文档
XWPFDocument document = new XWPFDocument();
// 创建多级编号样式
BigInteger numId = createMultiLevelNumbering(document);
// 创建关联编号的标题样式
createTitleStyle(document, "Heading1", "标题1", 18, 1, numId);
createTitleStyle(document, "Heading2", "标题2", 16, 2, numId);
createTitleStyle(document, "Heading3", "标题3", 14, 3, numId);
createTitleStyle(document, "Heading4", "标题4", 12, 4, numId);
createTitleStyle(document, "Heading5", "标题5", 12, 5, numId);
// 写入标题"测试用例"
XWPFParagraph title = document.createParagraph();
title.setAlignment(ParagraphAlignment.CENTER); // 居中
XWPFRun titleRun = title.createRun();
titleRun.setText("测试用例");
titleRun.setFontSize(22); // 字体大小
titleRun.setFontFamily("宋体"); // 字体样式
// 增加固定格式内容
fillFixedContent(document,projectId, treeSelects, idList);
// 设置响应头
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
response.setHeader("Content-Disposition", "attachment; filename=testCase_" + projectId + ".docx");
// 将文档写入响应输出流
OutputStream outputStream = response.getOutputStream();
document.write(outputStream);
outputStream.flush();
outputStream.close();
document.close();
}
// 导入测试用例相关
/**
* 处理docx文件将测试用例导入到数据库
@ -1376,11 +1555,69 @@ public class PmsProjectTestcaseServiceImpl implements IPmsProjectTestcaseService
*/
private boolean testcaseTableValidation(List<List<String>> tableRows) {
if (tableRows.size()>12) {
return tableRows.get(0).size() == 2 && tableRows.get(1).size() == 4 && tableRows.get(8).size() == 5;
return (tableRows.get(0).size() == 2 && tableRows.get(1).size() == 4 && tableRows.get(8).size() == 5) ||
(tableRows.get(0).size() == 9 && tableRows.get(1).size() == 9 && tableRows.get(8).size() == 9);
}
return false;
}
private PmsProjectTestcase testcaseSetVal(List<List<String>> tableRows, Long projectId, boolean isTablerule) {
PmsProjectTestcase testcase = new PmsProjectTestcase();
List<PmsProjectTestcaseStep> testcaseSteps = new ArrayList<>();
if (isTablerule) {
testcase.setPmsProjectId(projectId);
testcase.setTitle(getTableCellValue(tableRows,0,3));
testcase.setIdentifier(getTableCellValue(tableRows,1,3));
testcase.setDescription(getTableCellValue(tableRows,2,3));
testcase.setTestMethod(getTableCellValue(tableRows,3,3));
String testType = getTableCellValue(tableRows,4,3);
testcase.setTypeId(TypeToId(testType));
testcase.setPreconditions(getTableCellValue(tableRows,5,3));
testcase.setTerminationConditions(getTableCellValue(tableRows,6,3));
for(int i = 9; i < tableRows.size(); i++){
if (!Objects.equals(getTableCellValue(tableRows, i, 0), "设计人员")){
PmsProjectTestcaseStep testcaseStep = new PmsProjectTestcaseStep();
testcaseStep.setIndex(i - 8L);
testcaseStep.setContent(getTableCellValue(tableRows, i, 1));
testcaseStep.setExpectedResult(getTableCellValue(tableRows, i, 4));
testcaseSteps.add(testcaseStep);
} else {
testcase.setCreateBy(getTableCellValue(tableRows, i, 2));
Date date = stringToDateUsingDateTimeFormatter(getTableCellValue(tableRows, i , 6), "yyyy-MM-dd");
testcase.setCreateTime(date);
break;
}
}
}else{
testcase.setPmsProjectId(projectId);
testcase.setTitle(getTableCellValue(tableRows,0,1));
testcase.setIdentifier(getTableCellValue(tableRows,1,1));
testcase.setDescription(getTableCellValue(tableRows,2,1));
testcase.setTestMethod(getTableCellValue(tableRows,3,1));
String testType = getTableCellValue(tableRows,4,1);
testcase.setTypeId(TypeToId(testType));
testcase.setPreconditions(getTableCellValue(tableRows,5,1));
testcase.setTerminationConditions(getTableCellValue(tableRows,6,1));
for(int i = 9; i < tableRows.size(); i++){
if (tableRows.get(i).size() > 4 && getTableCellValue(tableRows, i, 1) != null){
PmsProjectTestcaseStep testcaseStep = new PmsProjectTestcaseStep();
testcaseStep.setIndex(i - 8L);
testcaseStep.setContent(getTableCellValue(tableRows, i, 1));
testcaseStep.setExpectedResult(getTableCellValue(tableRows, i, 2));
testcaseSteps.add(testcaseStep);
} else {
testcase.setCreateBy(getTableCellValue(tableRows, i, 1));
Date date = stringToDateUsingDateTimeFormatter(getTableCellValue(tableRows, i , 3), "yyyy-MM-dd");
testcase.setCreateTime(date);
break;
}
}
}
testcase.setIsImport(1L);
testcase.setTestcaseStepList(testcaseSteps);
return testcase;
}
/**
* 将集合中表格行列数据写入数据库中
* @param allTables 所有表格的行列数据
@ -1395,33 +1632,7 @@ public class PmsProjectTestcaseServiceImpl implements IPmsProjectTestcaseService
List<List<String>> tableRows = tableMap.get(tableName);
System.out.println("Table " + tableName + " rows: " + tableRows.size());
if(testcaseTableValidation(tableRows)){
PmsProjectTestcase testcase = new PmsProjectTestcase();
List<PmsProjectTestcaseStep> testcaseSteps = new ArrayList<>();
testcase.setPmsProjectId(projectId);
testcase.setTitle(getTableCellValue(tableRows,0,1));
testcase.setIdentifier(getTableCellValue(tableRows,1,1));
testcase.setDescription(getTableCellValue(tableRows,2,1));
testcase.setTestMethod(getTableCellValue(tableRows,3,1));
String testType = getTableCellValue(tableRows,4,1);
testcase.setTypeId(TypeToId(testType));
testcase.setPreconditions(getTableCellValue(tableRows,5,1));
testcase.setTerminationConditions(getTableCellValue(tableRows,6,1));
for(int i = 9; i < tableRows.size(); i++){
if (tableRows.get(i).size() > 4 && getTableCellValue(tableRows, i, 1) != null){
PmsProjectTestcaseStep testcaseStep = new PmsProjectTestcaseStep();
testcaseStep.setIndex(i - 8L);
testcaseStep.setContent(getTableCellValue(tableRows, i, 1));
testcaseStep.setExpectedResult(getTableCellValue(tableRows, i, 2));
testcaseSteps.add(testcaseStep);
} else {
testcase.setCreateBy(getTableCellValue(tableRows, i, 1));
Date date = stringToDateUsingDateTimeFormatter(getTableCellValue(tableRows, i , 3), "yyyy-MM-dd");
testcase.setCreateTime(date);
break;
}
}
testcase.setIsImport(1L);
testcase.setTestcaseStepList(testcaseSteps);
PmsProjectTestcase testcase = testcaseSetVal(tableRows, projectId, tableRows.get(0).size() == 9);
if (testcase.getAssigneeGitlinkId() == null) {
// 未设置维护用户时默认为当前操作用户