diff --git a/microservices-modules/microservices-modules-pms/pom.xml b/microservices-modules/microservices-modules-pms/pom.xml
index 041a97b79..33d86dfc1 100644
--- a/microservices-modules/microservices-modules-pms/pom.xml
+++ b/microservices-modules/microservices-modules-pms/pom.xml
@@ -121,6 +121,12 @@
httpclient
4.5.14
+
+
+
+
+
+
com.hankcs
@@ -139,6 +145,12 @@
0.4.8
compile
+
+
+
+
+
+
diff --git a/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/product/service/IPmsProductModuleService.java b/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/product/service/IPmsProductModuleService.java
index 0493a6628..cdb39e1e7 100644
--- a/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/product/service/IPmsProductModuleService.java
+++ b/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/product/service/IPmsProductModuleService.java
@@ -67,6 +67,15 @@ public interface IPmsProductModuleService
*/
List selectPmsProductModuleTreeList(PmsProductModule pmsProductModule, int type);
+ /**
+ * 构建模块的树状结构
+ *
+ * @param projectid 模块搜索条件
+ * @param type 模块类型(1产品模块 2测试用例模块, 3测试单,实际取测试用例模块)
+ * @return 模块树状结构
+ */
+ List getPmsProductModuleTreeList(Long projectid, int type);
+
/**
* 查询模块及模块子级ID列表
* @param parentModuleId 父级模块ID
diff --git a/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/product/service/impl/PmsProductModuleServiceImpl.java b/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/product/service/impl/PmsProductModuleServiceImpl.java
index 69699d590..53e975621 100644
--- a/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/product/service/impl/PmsProductModuleServiceImpl.java
+++ b/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/product/service/impl/PmsProductModuleServiceImpl.java
@@ -351,6 +351,63 @@ public class PmsProductModuleServiceImpl implements IPmsProductModuleService
return buildModuleTreeSelect(moduleList);
}
+ @Override
+ public List getPmsProductModuleTreeList(Long projectId, int type) {
+ List moduleList;
+ PmsProductModule pmsProductModule = new PmsProductModule();
+ pmsProductModule.setPmsProjectId(projectId);
+ //模块类型(1产品模块 2测试用例模块, 3测试单,实际取测试用例模块)
+ if (PmsConstants.TESTSHEET_TAG_AND_MODULE_TYPE.equals(type)) {
+ pmsProductModule.setType(PmsConstants.TESTCASE_TAG_AND_MODULE_TYPE);
+ moduleList = pmsProductModuleMapper.selectPmsProductModuleForTestsheetList(pmsProductModule);
+ } else {
+ pmsProductModule.setType(type);
+ moduleList = pmsProductModuleMapper.selectPmsProductModuleList(pmsProductModule);
+ }
+
+ Integer allNodeDataCount;
+ Integer noModuleNodeDataCount;
+ String allNodeName = "全部模块";
+ if (type == PmsConstants.PRODUCT_TAG_AND_MODULE_TYPE) {
+ // 设置各节点数量为需求数量
+ moduleList.forEach(x -> x.setNodeDataCount(x.getRequirementCount()));
+
+
+ } else if (type == PmsConstants.TESTSHEET_TAG_AND_MODULE_TYPE) {
+ // 设置各节点数量为测试单执行用例数量
+ moduleList.forEach(x -> x.setNodeDataCount(x.getTestsheetCasesCount()));
+ // 排除0测试单执行用例数据
+ moduleList = moduleList.stream().filter(e -> e.getTestsheetCasesCount() > 0).collect(Collectors.toList());
+ } else {
+ // 设置各节点数量为测试用例数量
+ moduleList.forEach(x -> x.setNodeDataCount(x.getTestCaseCount()));
+ }
+ // 父级需求数需包含子级需求数
+ aggregateCounts(moduleList, 0L);
+
+ // 构建顶级模块(全部需求)
+ PmsProductModule allPmsProductModule=new PmsProductModule();
+ allPmsProductModule.setPmsProductIdentifier(pmsProductModule.getPmsProductIdentifier());
+ allPmsProductModule.setId(0L);
+ allPmsProductModule.setModuleName(allNodeName);
+ allPmsProductModule.setIsFixedModule(true);
+ moduleList.add(allPmsProductModule);
+ // 模块根据id排序
+ moduleList = moduleList.stream()
+ .sorted(Comparator.comparing(PmsProductModule::getId))
+ .sorted(Comparator.comparing(PmsProductModule::getIsFixedModule))
+ .collect(Collectors.toList());
+ // 构建无所属模块
+ PmsProductModule noPmsProductModule = new PmsProductModule();
+ noPmsProductModule.setPmsProductIdentifier(pmsProductModule.getPmsProductIdentifier());
+ noPmsProductModule.setId(-1L);
+ noPmsProductModule.setModuleName(NO_MODULE(type));
+ noPmsProductModule.setParentId(0L);
+ noPmsProductModule.setIsFixedModule(true);
+ moduleList.add(noPmsProductModule);
+ return buildModuleTreeSelect(moduleList);
+ }
+
@Override
public List selectChildrenPmsProductModuleIdList(Long parentModuleId) {
List pmsProductModuleList=new ArrayList<>();
diff --git a/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/controller/PmsProjectTestcaseController.java b/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/controller/PmsProjectTestcaseController.java
index 751038176..da1d6f05a 100644
--- a/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/controller/PmsProjectTestcaseController.java
+++ b/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/controller/PmsProjectTestcaseController.java
@@ -9,15 +9,20 @@ import com.microservices.common.log.annotation.Log;
import com.microservices.common.log.enums.BusinessType;
import com.microservices.common.security.annotation.Logical;
import com.microservices.common.security.annotation.RequiresPermissions;
+import com.microservices.pms.product.domain.vo.TreeSelect;
import com.microservices.pms.product.service.IPmsProductModuleService;
import com.microservices.pms.project.domain.PmsProjectTestcase;
import com.microservices.pms.project.domain.vo.*;
import com.microservices.pms.project.service.IPmsProjectTestcaseService;
+import com.microservices.pms.utils.PmsConstants;
import io.swagger.annotations.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
import java.util.List;
/**
@@ -132,4 +137,40 @@ public class PmsProjectTestcaseController extends BaseController {
@RequestBody JSONObject batchUpdateVo) {
return toAjax(pmsProjectTestcaseService.batchUpdatePmsProjectTestcase(batchUpdateVo));
}
+
+ /**
+ * 批量下载测试用例
+ */
+ @ApiOperation(value = "批量下载测试用例")
+ @GetMapping("/{projectId}/download")
+ public void download(@PathVariable("projectId") Long projectId, HttpServletResponse response)throws IOException {
+ PmsProjectTestcaseModuleSearchVo pmsProjectTestcaseModuleSearchVo = new PmsProjectTestcaseModuleSearchVo();
+ pmsProjectTestcaseModuleSearchVo.setPmsProjectId(projectId);
+ List treeSelects = pmsProductModuleService.getPmsProductModuleTreeList(projectId, PmsConstants.TESTCASE_TAG_AND_MODULE_TYPE);
+ pmsProjectTestcaseService.generateWord(treeSelects, projectId, response);
+ }
+
+ /**
+ * 批量导入测试用例
+ */
+ @ApiOperation(value = "批量导入测试用例")
+ @PostMapping("/{projectId}/upload")
+ public AjaxResult upload(@RequestPart("file") MultipartFile file, @PathVariable("projectId") Long projectId) throws IOException {
+ if (file.isEmpty()) {
+ return error("上传的文件为空,请选择有效的文件");
+ }
+
+ String fileName = file.getOriginalFilename();
+ if (fileName == null) {
+ return error("无法获取文件名,请检查上传的文件");
+ }
+
+ if (fileName.endsWith(".docx")) {
+ pmsProjectTestcaseService.testcaseImport(projectId, file);
+ return success("上传的文件是 docx 格式");
+ } else {
+ return error("上传的文件不是 docx 格式,请上传 docx 文件");
+ }
+ }
+
}
diff --git a/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/domain/PmsProjectTestcase.java b/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/domain/PmsProjectTestcase.java
index 5cc54fc38..38fd8274c 100644
--- a/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/domain/PmsProjectTestcase.java
+++ b/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/domain/PmsProjectTestcase.java
@@ -126,29 +126,35 @@ public class PmsProjectTestcase extends BaseEntity {
private List testcaseStepList;
/**
- * 预留字段1
+ * 测试用例描述
*/
- @JsonIgnore
- private String reservedField1;
+ @ApiModelProperty(value = "测试用例描述")
+ private String description;
/**
- * 预留字段2
+ * 测试方法
*/
- @JsonIgnore
- private String reservedField2;
+ @ApiModelProperty(value = "测试方法")
+ private String testMethod;
/**
- * 预留字段3
+ * 测试终止条件
*/
- @JsonIgnore
- private String reservedField3;
+ @ApiModelProperty(value = "测试终止条件")
+ private String terminationConditions;
@ApiModelProperty(value = "测试用例标识")
private String identifier;
+ @ApiModelProperty(value = "测试追踪")
+ private String testTracking;
+
@ApiModelProperty(value = "产品需求id")
private Long productReqSpecsId;
+ @ApiModelProperty(value = "是否导入")
+ private Long isImport;
+
public PmsProjectTestcaseDataVo toPmsProjectTestcaseDataVo() {
PmsProjectTestcaseDataVo target = new PmsProjectTestcaseDataVo();
BeanUtils.copyProperties(this, target);
diff --git a/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/domain/vo/PendingOutputDataVo.java b/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/domain/vo/PendingOutputDataVo.java
new file mode 100644
index 000000000..d3b39ecb7
--- /dev/null
+++ b/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/domain/vo/PendingOutputDataVo.java
@@ -0,0 +1,68 @@
+package com.microservices.pms.project.domain.vo;
+
+import lombok.Data;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.List;
+
+@Data
+public class PendingOutputDataVo {
+ private Long id;
+
+ private Long pms_projiect_id;
+
+ private String title;
+
+ private Long type_id;
+
+ private String tag_ids;
+
+ private List content_list;
+
+ private List expected_result;
+
+ private String update_by;
+
+ private LocalDate update_time;
+
+ private String create_by;
+
+ private LocalDate create_time;
+
+ private String test_type;
+
+ private String description;
+
+ private String test_method;
+
+ private String termination_conditions;
+
+ private String identifier;
+
+ private String preconditions;
+
+
+
+ public PendingOutputDataVo() {
+ content_list = new ArrayList<>();
+ expected_result = new ArrayList<>();
+ }
+
+ // 添加元素的方法
+ public void addToContent(String item) {
+ content_list.add(item);
+ }
+
+ public void addToExpected(String item) {
+ expected_result.add(item);
+ }
+
+ public void addContent(String item) {
+ this.content_list.add(item);
+ }
+
+ public void addExpected(String item) {
+ this.expected_result.add(item);
+ }
+}
diff --git a/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/domain/vo/PmsProjectTestcaseDetailVo.java b/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/domain/vo/PmsProjectTestcaseDetailVo.java
index 1cbff13b7..14b6d6738 100644
--- a/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/domain/vo/PmsProjectTestcaseDetailVo.java
+++ b/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/domain/vo/PmsProjectTestcaseDetailVo.java
@@ -5,6 +5,7 @@ import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
+import javax.validation.constraints.Size;
import java.util.List;
/**
@@ -35,6 +36,27 @@ public class PmsProjectTestcaseDetailVo extends PmsProjectTestcaseDataVo {
@ApiModelProperty(value = "备注")
private String remark;
+ /**
+ * 测试用例描述
+ */
+ @ApiModelProperty(value = "测试用例描述")
+ @Size(max = 500, message = "长度需要小于500")
+ private String description;
+
+ /**
+ * 测试方法
+ */
+ @ApiModelProperty(value = "测试方法")
+ @Size(max = 255, message = "长度需要小于255")
+ private String testMethod;
+
+ /**
+ * 测试终止条件
+ */
+ @ApiModelProperty(value = "测试终止条件")
+ @Size(max = 500, message = "长度需要小于500")
+ private String terminationConditions;
+
/**
* 测试步骤列表
*/
diff --git a/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/domain/vo/PmsProjectTestcaseInputVo.java b/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/domain/vo/PmsProjectTestcaseInputVo.java
index 05aa4ab44..66c93cf29 100644
--- a/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/domain/vo/PmsProjectTestcaseInputVo.java
+++ b/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/domain/vo/PmsProjectTestcaseInputVo.java
@@ -84,6 +84,35 @@ public class PmsProjectTestcaseInputVo {
@Size(max = 500, message = "长度需要小于500")
private String remark;
+ /**
+ * 测试用例描述
+ */
+ @ApiModelProperty(value = "测试用例描述")
+ @Size(max = 500, message = "长度需要小于500")
+ private String description;
+
+ /**
+ * 测试方法
+ */
+ @ApiModelProperty(value = "测试方法")
+ @Size(max = 255, message = "长度需要小于255")
+ private String testMethod;
+
+ /**
+ * 测试终止条件
+ */
+ @ApiModelProperty(value = "测试终止条件")
+ @Size(max = 500, message = "长度需要小于500")
+ private String terminationConditions;
+
+ @ApiModelProperty(value = "测试用例标识")
+ @Size(max = 255, message = "长度需要小于500")
+ private String identifier;
+
+ @ApiModelProperty(value = "测试追踪")
+ @Size(max = 255, message = "长度需要小于500")
+ private String testTracking;
+
/**
* 测试步骤列表
*/
diff --git a/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/domain/vo/PmsProjectTestcaseStepVo.java b/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/domain/vo/PmsProjectTestcaseStepVo.java
new file mode 100644
index 000000000..4d04e4666
--- /dev/null
+++ b/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/domain/vo/PmsProjectTestcaseStepVo.java
@@ -0,0 +1,17 @@
+package com.microservices.pms.project.domain.vo;
+
+import lombok.Data;
+
+@Data
+public class PmsProjectTestcaseStepVo {
+ private Long index;
+
+ private String content;
+
+ private String expectedResult;
+
+ private String updateBy;
+
+ private String createBy;
+
+}
diff --git a/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/domain/vo/PmsProjectTestcaseUpdateVo.java b/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/domain/vo/PmsProjectTestcaseUpdateVo.java
index f6f8ed78b..b1837ac5f 100644
--- a/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/domain/vo/PmsProjectTestcaseUpdateVo.java
+++ b/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/domain/vo/PmsProjectTestcaseUpdateVo.java
@@ -80,6 +80,27 @@ public class PmsProjectTestcaseUpdateVo {
@Size(max = 500, message = "长度需要小于500")
private String remark;
+ /**
+ * 测试用例描述
+ */
+ @ApiModelProperty(value = "测试用例描述")
+ @Size(max = 500, message = "长度需要小于500")
+ private String description;
+
+ /**
+ * 测试方法
+ */
+ @ApiModelProperty(value = "测试方法")
+ @Size(max = 255, message = "长度需要小于255")
+ private String testMethod;
+
+ /**
+ * 测试终止条件
+ */
+ @ApiModelProperty(value = "测试终止条件")
+ @Size(max = 500, message = "长度需要小于500")
+ private String terminationConditions;
+
/**
* 测试步骤列表
*/
diff --git a/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/domain/vo/PmsProjectTestcaseVo.java b/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/domain/vo/PmsProjectTestcaseVo.java
new file mode 100644
index 000000000..a220d37d9
--- /dev/null
+++ b/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/domain/vo/PmsProjectTestcaseVo.java
@@ -0,0 +1,41 @@
+package com.microservices.pms.project.domain.vo;
+
+import lombok.Data;
+
+import java.time.LocalDate;
+
+@Data
+public class PmsProjectTestcaseVo {
+ private Long id;
+
+ private Long pmsProjiectId;
+
+ private String title;
+
+ private Long typeId;
+
+ private String tagIds;
+
+ private Long pmsModuleId;
+
+ private LocalDate updateTime;
+
+ private String updateBy;
+
+ private LocalDate createTime;
+
+ private String createBy;
+
+ private String preconditions;
+
+ private String description;
+
+ private String testMethod;
+
+ private String terminationConditions;
+
+ private String identifier;
+
+ private String testTracking;
+
+}
diff --git a/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/mapper/PmsProjectTestcaseMapper.java b/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/mapper/PmsProjectTestcaseMapper.java
index f5549ae80..64b80c1e5 100644
--- a/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/mapper/PmsProjectTestcaseMapper.java
+++ b/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/mapper/PmsProjectTestcaseMapper.java
@@ -2,6 +2,7 @@ package com.microservices.pms.project.mapper;
import com.microservices.pms.project.domain.PmsProjectTestcase;
import com.microservices.pms.project.domain.vo.PmsProjectTestcaseBatchUpdateVo;
+import com.microservices.pms.project.domain.vo.PmsProjectTestcaseVo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@@ -112,4 +113,12 @@ public interface PmsProjectTestcaseMapper {
List selectProjectIdListInTestcaseIdList(@Param("idList") List idList);
int deletePmsProjectTestcaseByProjectId(@Param("projectId") Long projectId);
+
+ /**
+ * 获取testcasevo中部分字段
+ *
+ * @param pms_project_id 关联的项目号
+ */
+ public List getTestcase(Long pms_project_id);
+
}
diff --git a/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/mapper/PmsProjectTestcaseStepMapper.java b/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/mapper/PmsProjectTestcaseStepMapper.java
index 8f7b74815..13a48ab27 100644
--- a/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/mapper/PmsProjectTestcaseStepMapper.java
+++ b/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/mapper/PmsProjectTestcaseStepMapper.java
@@ -1,6 +1,7 @@
package com.microservices.pms.project.mapper;
import com.microservices.pms.project.domain.PmsProjectTestcaseStep;
+import com.microservices.pms.project.domain.vo.PmsProjectTestcaseStepVo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@@ -77,4 +78,11 @@ public interface PmsProjectTestcaseStepMapper {
* @return 操作步骤
*/
PmsProjectTestcaseStep selectPmsProjectTestcaseStepByIndexAndTestcaseId(@Param("index") Long index, @Param("testcaseId") Long testcaseId);
+
+ /**
+ * 获取testCaseStepvo中部分字段
+ *
+ * @param PmsProjectTestcase_id 关联的测试用例号
+ */
+ public List getTestcaseStep(Long PmsProjectTestcase_id);
}
diff --git a/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/service/IPmsProjectTestcaseService.java b/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/service/IPmsProjectTestcaseService.java
index 052fca224..7bbcd9d2c 100644
--- a/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/service/IPmsProjectTestcaseService.java
+++ b/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/service/IPmsProjectTestcaseService.java
@@ -3,10 +3,14 @@ package com.microservices.pms.project.service;
import com.alibaba.fastjson2.JSONObject;
import com.microservices.common.core.web.page.GenericsTableDataInfo;
import com.microservices.pms.product.domain.vo.ProductReqSpecsAssociatedTestcaseDataVo;
+import com.microservices.pms.product.domain.vo.TreeSelect;
import com.microservices.pms.project.domain.PmsProjectTestcase;
import com.microservices.pms.project.domain.vo.PmsProjectTestcaseDataVo;
import com.microservices.pms.project.domain.vo.PmsProjectTestcaseDetailVo;
+import org.springframework.web.multipart.MultipartFile;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
import java.util.List;
/**
@@ -118,4 +122,21 @@ public interface IPmsProjectTestcaseService {
List selectReqSpecsAssociatedTestcaseList(Long reqSpecsId);
PmsProjectTestcase copyPmsProjectTestcase(Long testcaseId);
+
+ /**
+ * 获取测试用例列表
+ *
+ * @param treeSelects 模块树结构
+ * @param projectId 项目id
+ * @param response
+ * @return 导出word数据流
+ */
+ void generateWord(List treeSelects, Long projectId, HttpServletResponse response)throws IOException;
+
+ /**
+ * 处理docx文件将测试用例导入到数据库
+ * @param projectId 项目id
+ * @param file 导入的docx文件
+ */
+ void testcaseImport(Long projectId, MultipartFile file) throws IOException;
}
diff --git a/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/service/impl/PmsProjectTestcaseServiceImpl.java b/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/service/impl/PmsProjectTestcaseServiceImpl.java
index 18d6a4072..770cf5b05 100644
--- a/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/service/impl/PmsProjectTestcaseServiceImpl.java
+++ b/microservices-modules/microservices-modules-pms/src/main/java/com/microservices/pms/project/service/impl/PmsProjectTestcaseServiceImpl.java
@@ -5,6 +5,7 @@ import com.microservices.common.core.exception.ServiceException;
import com.microservices.common.core.utils.DateUtils;
import com.microservices.common.core.utils.JSONUtils;
import com.microservices.common.core.utils.PageUtils;
+import com.microservices.common.core.utils.PinYinStringUtils;
import com.microservices.common.core.utils.bean.BeanUtils;
import com.microservices.common.core.web.page.GenericsTableDataInfo;
import com.microservices.common.security.utils.SecurityUtils;
@@ -15,15 +16,14 @@ import com.microservices.pms.product.domain.PmsProductModule;
import com.microservices.pms.product.domain.PmsProductRequirement;
import com.microservices.pms.product.domain.enums.ProductReqStatus;
import com.microservices.pms.product.domain.vo.ProductReqSpecsAssociatedTestcaseDataVo;
+import com.microservices.pms.product.domain.vo.TreeSelect;
import com.microservices.pms.product.service.IPmsProductModuleService;
import com.microservices.pms.product.service.IPmsProductRequirementService;
import com.microservices.pms.product.service.IPmsProductRequirementTagService;
import com.microservices.pms.project.domain.*;
-import com.microservices.pms.project.domain.vo.PmsProjectTestcaseBatchUpdateVo;
-import com.microservices.pms.project.domain.vo.PmsProjectTestcaseDataVo;
-import com.microservices.pms.project.domain.vo.PmsProjectTestcaseDetailVo;
-import com.microservices.pms.project.domain.vo.PmsProjectTestcaseStepDataVo;
+import com.microservices.pms.project.domain.vo.*;
import com.microservices.pms.project.mapper.PmsProjectTestcaseMapper;
+import com.microservices.pms.project.mapper.PmsProjectTestcaseStepMapper;
import com.microservices.pms.project.mapper.PmsProjectTestsheetCasesMapper;
import com.microservices.pms.project.service.IPmsProjectService;
import com.microservices.pms.project.service.IPmsProjectTestcaseService;
@@ -32,12 +32,25 @@ import com.microservices.pms.project.service.IPmsProjectTestcaseTypeService;
import com.microservices.pms.utils.PmsConstants;
import com.microservices.pms.utils.PmsUtils;
import com.microservices.system.api.domain.SysUser;
+import org.apache.poi.xwpf.usermodel.*;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.Field;
+import java.math.BigInteger;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static com.microservices.common.core.utils.PageUtils.startPage;
@@ -70,6 +83,8 @@ public class PmsProjectTestcaseServiceImpl implements IPmsProjectTestcaseService
private PmsProjectTestsheetCasesMapper pmsProjectTestsheetCasesMapper;
@Autowired
private IPmsProductRequirementService pmsProductRequirementService;
+ @Autowired
+ private PmsProjectTestcaseStepMapper pmsProjectTestcaseStepMapper;
/**
* 查询测试用例管理
@@ -83,7 +98,9 @@ public class PmsProjectTestcaseServiceImpl implements IPmsProjectTestcaseService
if (pmsProjectTestcase == null) {
throw new ServiceException("该测试用例不存在(测试用例ID[%s])", id);
}
- pmsProjectService.selectPmsProjectById(pmsProjectTestcase.getPmsProjectId());
+ if(pmsProjectTestcase.getIsImport() == null){
+ pmsProjectService.selectPmsProjectById(pmsProjectTestcase.getPmsProjectId());
+ }
return pmsProjectTestcase;
}
@@ -136,20 +153,22 @@ public class PmsProjectTestcaseServiceImpl implements IPmsProjectTestcaseService
, x -> pmsProductModuleService.selectPmsProductModuleById(x, false)
, pmsProjectTestcase.getPmsModuleId()));
}
- // 设置需求创建人
- pmsProjectTestcaseDataVo.setCreateBy(
- PmsUtils.getSelectObjectInCache(
- hashMap
- , pmsCommonService::getNickNameByUsername
- , pmsProjectTestcase.getCreateBy())
- );
- // 设置需求更新人
- pmsProjectTestcaseDataVo.setUpdateBy(
- PmsUtils.getSelectObjectInCache(
- hashMap
- , pmsCommonService::getNickNameByUsername
- , pmsProjectTestcase.getUpdateBy())
- );
+ if (pmsProjectTestcase.getIsImport() != null) {
+ // 设置需求创建人
+ pmsProjectTestcaseDataVo.setCreateBy(
+ PmsUtils.getSelectObjectInCache(
+ hashMap
+ , pmsCommonService::getNickNameByUsername
+ , pmsProjectTestcase.getCreateBy())
+ );
+ // 设置需求更新人
+ pmsProjectTestcaseDataVo.setUpdateBy(
+ PmsUtils.getSelectObjectInCache(
+ hashMap
+ , pmsCommonService::getNickNameByUsername
+ , pmsProjectTestcase.getUpdateBy())
+ );
+ }
// 设置产品需求
if (pmsProjectTestcase.getProductReqSpecsId() != null) {
pmsProjectTestcaseDataVo.setProductReqSpecs(
@@ -168,6 +187,9 @@ public class PmsProjectTestcaseServiceImpl implements IPmsProjectTestcaseService
PmsProjectTestcaseDetailVo pmsProjectTestcaseDetailVo = pmsProjectTestcaseDataVo.toPmsProjectTestcaseDetailVo();
pmsProjectTestcaseDetailVo.setPreconditions(pmsProjectTestcase.getPreconditions());
pmsProjectTestcaseDetailVo.setRemark(pmsProjectTestcase.getRemark());
+ pmsProjectTestcaseDetailVo.setDescription(pmsProjectTestcase.getDescription());
+ pmsProjectTestcaseDetailVo.setTestMethod(pmsProjectTestcase.getTestMethod());
+ pmsProjectTestcaseDetailVo.setTerminationConditions(pmsProjectTestcase.getTerminationConditions());
// 设置测试步骤列表
PmsProjectTestcaseStep pmsProjectTestcaseStepSearch = new PmsProjectTestcaseStep();
pmsProjectTestcaseStepSearch.setPmsTestcaseId(pmsProjectTestcase.getId());
@@ -175,7 +197,9 @@ public class PmsProjectTestcaseServiceImpl implements IPmsProjectTestcaseService
// 设置附件列表
pmsProjectTestcaseDetailVo.setFileList(pmsCommonService.getAllFileByFileIdentifiers(pmsProjectTestcase.getFileIdentifiers()));
- pmsCommonService.setBaseEntityVo(pmsProjectTestcaseDetailVo, pmsProjectTestcase);
+ if(pmsProjectTestcase.getIsImport() == null) {
+ pmsCommonService.setBaseEntityVo(pmsProjectTestcaseDetailVo, pmsProjectTestcase);
+ }
return pmsProjectTestcaseDetailVo;
}
@@ -493,4 +517,982 @@ public class PmsProjectTestcaseServiceImpl implements IPmsProjectTestcaseService
}
}
+
+ //测试用例导出相关
+ /**
+ * 从数据库osredem-microservices,表pms_project_testcase中获取测试用例信息存到集合中
+ *
+ * @param item 项目id
+ **/
+ public List getTestcase(Long item) {
+ return pmsProjectTestcaseMapper.getTestcase(item);
+ }
+
+ /**
+ * 从数据库osredem-microservices,表pms_project_testcase_step中获取测试步骤信息存到集合中
+ *
+ * @param item 表pms_project_testcase中index
+ **/
+ public List getTestcaseStep(Long item) {
+ return pmsProjectTestcaseStepMapper.getTestcaseStep(item);
+ }
+
+ private String typeIdtoSting(Long typeId) {
+ switch (Math.toIntExact(typeId)) {
+ case 1:
+ return "功能测试";
+ case 2:
+ return "性能测试";
+ case 3:
+ return "接口测试";
+ case 4:
+ return "安装部署";
+ case 5:
+ return "安全相关";
+ case 6:
+ return "配置相关";
+ default:
+ return "其他";
+ }
+ }
+ /**
+ * 将项目id下所有测试用例信息存到集合中
+ *
+ * @param item 项目id
+ **/
+ public List GetPendingOutputData(Long item, Long moduleId) {
+ List outputDatas = new ArrayList();
+ // 数据库中信息类存到testcases集合中
+ List testcases = getTestcase(item);
+
+ for (PmsProjectTestcaseVo testcase : testcases) {
+ Long pmsModuleId = testcase.getPmsModuleId();
+ boolean isEqual = Objects.equals(pmsModuleId, moduleId);
+ if (isEqual) {
+ List steps = getTestcaseStep(testcase.getId());
+ PendingOutputDataVo pendingOutputData = new PendingOutputDataVo();
+ pendingOutputData.setId(testcase.getId());
+ pendingOutputData.setTitle(testcase.getTitle());
+ pendingOutputData.setTag_ids(testcase.getTagIds());
+ pendingOutputData.setTest_type(typeIdtoSting(testcase.getTypeId()));
+ pendingOutputData.setTitle(testcase.getTitle());
+ pendingOutputData.setPreconditions(testcase.getPreconditions());
+ pendingOutputData.setUpdate_by(testcase.getUpdateBy());
+ pendingOutputData.setUpdate_time(testcase.getUpdateTime());
+ pendingOutputData.setCreate_by(testcase.getCreateBy());
+ pendingOutputData.setCreate_time(testcase.getCreateTime());
+ pendingOutputData.setDescription(testcase.getDescription());
+ pendingOutputData.setTest_method(testcase.getTestMethod());
+ pendingOutputData.setTermination_conditions(testcase.getTerminationConditions());
+ pendingOutputData.setIdentifier(testcase.getIdentifier());
+ if (steps != null) {
+ for (PmsProjectTestcaseStepVo step : steps) {
+ pendingOutputData.addContent(step.getContent());
+ pendingOutputData.addExpected(step.getExpectedResult());
+ }
+ }
+ outputDatas.add(pendingOutputData);
+ }
+ }
+ return outputDatas;
+ }
+
+
+ /**
+ * 水平合并单元格(跨列合并)
+ *
+ * @param wordExtractor 表格对象
+ * @param indentation 缩进值 1cm = 567
+ * @param text 写入单元格字符串
+ * @param isCenter 表格中段落是否居中
+ * @param isBold 表格中段落是否加租
+ */
+ private void autoWrap(XWPFTableCell wordExtractor, Integer indentation, String text, boolean isCenter, boolean isBold) {
+ XWPFParagraph paragraph = wordExtractor.getParagraphs().get(0);
+ // 设置单元格文本垂直居中
+ CTTcPr tcPr = wordExtractor.getCTTc().addNewTcPr();
+ CTVerticalJc va = tcPr.addNewVAlign();
+ va.setVal(STVerticalJc.CENTER);
+ // 设置段前段后间距
+ CTP ctp = paragraph.getCTP();
+ CTPPr pPr = ctp.isSetPPr() ? ctp.getPPr() : ctp.addNewPPr();
+ CTSpacing spacing = pPr.isSetSpacing() ? pPr.getSpacing() : pPr.addNewSpacing();
+ spacing.setBefore(BigInteger.valueOf(0)); // 段前间距
+ spacing.setAfter(BigInteger.valueOf(0));
+ //设置居中
+ if (isCenter) {
+ paragraph.setAlignment(ParagraphAlignment.CENTER);
+ }
+ // 设置缩进
+ CTInd ind = pPr.isSetInd() ? pPr.getInd() : pPr.addNewInd();
+ ind.setLeft(BigInteger.valueOf(indentation)); // 1cm = 567
+ // 设置自动换行
+ CTPPr pPr2 = paragraph.getCTP().addNewPPr();
+ CTSpacing spacing2 = pPr2.addNewSpacing();
+ spacing2.setLineRule(STLineSpacingRule.AUTO);
+ // 设置字体样式
+ XWPFRun run = paragraph.createRun();
+ run.setText(text);//填写单元格内容
+ //设置字体为五号 10.5*2
+ CTRPr rPr = run.getCTR().isSetRPr() ? run.getCTR().getRPr() : run.getCTR().addNewRPr();
+ rPr.addNewSz().setVal(BigInteger.valueOf(21));
+ rPr.addNewSzCs().setVal(BigInteger.valueOf(21));
+ // 字体样式
+ run.setFontFamily("Times New Roman");
+ run.setFontFamily("宋体", XWPFRun.FontCharRange.eastAsia);
+ run.setBold(isBold); // 是否加粗
+ }
+
+ /**
+ * 水平合并单元格(跨列合并)
+ *
+ * @param table 表格对象
+ * @param row 行索引
+ * @param startCol 起始列索引
+ * @param endCol 结束列索引
+ */
+ private static void mergeCellsHorizontally(XWPFTable table, int row, int startCol, int endCol) {
+ for (int cellIndex = startCol; cellIndex <= endCol; cellIndex++) {
+ XWPFTableCell cell = table.getRow(row).getCell(cellIndex);
+ if (cellIndex == startCol) {
+ // 第一个单元格设置合并范围
+ cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
+ } else {
+ // 后续单元格标记为继续合并
+ cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
+ }
+ }
+ }
+
+ /**
+ * 隐藏表格的最后一行
+ *
+ * @param xwpfTabletable 表格对象
+ */
+ private void hideTableRow(XWPFTable xwpfTabletable) {
+ XWPFTableRow templateRow = xwpfTabletable.getRow(xwpfTabletable.getNumberOfRows()-1); // 使用最后一行作为模板
+ CTTrPr trPr = templateRow.getCtRow().isSetTrPr() ? templateRow.getCtRow().getTrPr() : templateRow.getCtRow().addNewTrPr();
+ trPr.addNewHidden();//.setVal("on"); // 隐藏模板行
+
+ //隐藏行中段落
+ for (XWPFTableCell cell : templateRow.getTableCells()) {
+ for (XWPFParagraph paragraph : cell.getParagraphs()) {
+ CTPPr pPr = paragraph.getCTP().isSetPPr() ?
+ paragraph.getCTP().getPPr() :
+ paragraph.getCTP().addNewPPr();
+ pPr.addNewRPr().addNewVanish();//.setVal("on");
+ }
+ }
+ }
+
+
+ /**
+ * 设置表格边框格式
+ *
+ * @param xwpfTabletable 表格对象
+ */
+ private void setTableBorders(XWPFTable xwpfTabletable) {
+ CTTblPr tblPr = xwpfTabletable.getCTTbl().getTblPr();
+ CTTblBorders borders = tblPr.isSetTblBorders() ? tblPr.getTblBorders() : tblPr.addNewTblBorders();
+
+ // 设置上边框
+ CTBorder top = borders.isSetTop() ? borders.getTop() : borders.addNewTop();
+ top.setVal(STBorder.SINGLE); // 边框类型:单线
+ top.setSz(BigInteger.valueOf(12)); // 边框大小:8 = 1pt
+ top.setColor("000000"); // 边框颜色:黑色
+
+ // 设置下边框
+ CTBorder bottom = borders.isSetBottom() ? borders.getBottom() : borders.addNewBottom();
+ bottom.setVal(STBorder.SINGLE);
+ bottom.setSz(BigInteger.valueOf(6));
+ bottom.setColor("000000");
+
+ // 设置左边框
+ CTBorder left = borders.isSetLeft() ? borders.getLeft() : borders.addNewLeft();
+ left.setVal(STBorder.SINGLE);
+ left.setSz(BigInteger.valueOf(12));
+ left.setColor("000000");
+
+ // 设置右边框
+ CTBorder right = borders.isSetRight() ? borders.getRight() : borders.addNewRight();
+ right.setVal(STBorder.SINGLE);
+ right.setSz(BigInteger.valueOf(12));
+ right.setColor("000000");
+
+ // 设置内部水平边框
+ CTBorder insideH = borders.isSetInsideH() ? borders.getInsideH() : borders.addNewInsideH();
+ insideH.setVal(STBorder.SINGLE);
+ insideH.setSz(BigInteger.valueOf(6)); // 比外边框细一些
+ insideH.setColor("000000");
+
+ // 设置内部垂直边框
+ CTBorder insideV = borders.isSetInsideV() ? borders.getInsideV() : borders.addNewInsideV();
+ insideV.setVal(STBorder.SINGLE);
+ insideV.setSz(BigInteger.valueOf(6));
+ insideV.setColor("000000");
+ }
+
+ private void fillTbale(XWPFTable xwpfTable, PendingOutputDataVo pendingOutputData) {
+ for (XWPFTableRow row : xwpfTable.getRows()) {
+ // 遍历行中的每个单元格
+ for (XWPFTableCell cell : row.getTableCells()) {
+ XWPFParagraph paragraph = cell.getParagraphs().get(0);
+ // 设置单元格文本垂直居中
+ CTTcPr tcPr = cell.getCTTc().addNewTcPr();
+ CTVerticalJc va = tcPr.addNewVAlign();
+ va.setVal(STVerticalJc.CENTER);
+ // 设置段前段后间距
+ CTP ctp = paragraph.getCTP();
+ CTPPr pPr = ctp.isSetPPr() ? ctp.getPPr() : ctp.addNewPPr();
+ CTSpacing spacing = pPr.isSetSpacing() ? pPr.getSpacing() : pPr.addNewSpacing();
+ spacing.setBefore(BigInteger.valueOf(0)); // 段前间距
+ spacing.setAfter(BigInteger.valueOf(0));
+ }
+ }
+ //固定文本部分
+ autoWrap(xwpfTable.getRow(0).getCell(0), 125,"测试用例名称",false,true);
+ autoWrap(xwpfTable.getRow(1).getCell(0), 125,"测试用例标识",false,true);
+ autoWrap(xwpfTable.getRow(1).getCell(5), 125,"测试追踪",false,true);
+ autoWrap(xwpfTable.getRow(2).getCell(0), 125,"测试用例描述",false,true);
+ autoWrap(xwpfTable.getRow(3).getCell(0), 125,"测试方法",false,true);
+ autoWrap(xwpfTable.getRow(4).getCell(0), 125,"测试类型",false,true);
+ autoWrap(xwpfTable.getRow(5).getCell(0), 62,"前提和约束(包括初始化要求)",false,true);
+ autoWrap(xwpfTable.getRow(6).getCell(0), 125,"测试终止条件",false,true);
+ autoWrap(xwpfTable.getRow(7).getCell(0), 125,"测试过程",true,true);
+ autoWrap(xwpfTable.getRow(8).getCell(0), 125,"序号",false,true);
+ autoWrap(xwpfTable.getRow(8).getCell(1), 125,"输入及操作步骤",true,true);
+ autoWrap(xwpfTable.getRow(8).getCell(4), 125,"期望测试结果",true,true);
+ autoWrap(xwpfTable.getRow(8).getCell(6), 125,"评估准则",true,true);
+ autoWrap(xwpfTable.getRow(8).getCell(8), 125,"实际测试结果",true,true);
+ autoWrap(xwpfTable.getRow(xwpfTable.getNumberOfRows() - 4).getCell(0), 62,"设计人员",false,true);
+ autoWrap(xwpfTable.getRow(xwpfTable.getNumberOfRows() - 4).getCell(4), 62,"设计日期",false,true);
+ autoWrap(xwpfTable.getRow(xwpfTable.getNumberOfRows() - 3).getCell(0), 62,"测试人员",false,true);
+ autoWrap(xwpfTable.getRow(xwpfTable.getNumberOfRows() - 3).getCell(4), 62,"测试执行时间",false,true);
+ autoWrap(xwpfTable.getRow(xwpfTable.getNumberOfRows() - 2).getCell(0), 62,"执行结果",false,true);
+
+ //需要通过调取数据库中信息填入部分
+ autoWrap(xwpfTable.getRow(0).getCell(3), 62,pendingOutputData.getTitle(),false,false);
+ autoWrap(xwpfTable.getRow(1).getCell(3), 62,pendingOutputData.getIdentifier(),false,false);
+ autoWrap(xwpfTable.getRow(2).getCell(3), 62,pendingOutputData.getDescription(),false,false);
+ autoWrap(xwpfTable.getRow(3).getCell(3), 62,pendingOutputData.getTest_method(),false,false);
+ autoWrap(xwpfTable.getRow(4).getCell(3), 62,pendingOutputData.getTest_type(),false,false);
+ autoWrap(xwpfTable.getRow(5).getCell(3), 62,pendingOutputData.getPreconditions(),false,false);
+ autoWrap(xwpfTable.getRow(6).getCell(3), 62,pendingOutputData.getTermination_conditions(),false,false);
+ List content = pendingOutputData.getContent_list();
+ List expected = pendingOutputData.getExpected_result();
+ int i;
+ for(i = 0;i columnWidths = new ArrayList<>();
+ columnWidths.add("7.6%");
+ columnWidths.add("11%");
+ columnWidths.add("4.8%");
+ columnWidths.add("11.1%");
+ columnWidths.add("19.8%");
+ columnWidths.add("7.3%");
+ columnWidths.add("9%");
+ columnWidths.add("9.1%");
+ columnWidths.add("20.1%");
+
+ XWPFTableRow firstRow = table.getRow(0);
+ for (int rowIndex = 0; rowIndex < table.getNumberOfRows(); rowIndex++) {
+ XWPFTableRow row = table.getRow(rowIndex);
+ for (int colIndex = 0; colIndex < columnWidths.size(); colIndex++) {
+ row.getCell(colIndex).setWidth(columnWidths.get(colIndex));
+ }
+ }
+
+ // 生成模板第1行(通过合并方式)
+ mergeCellsHorizontally(table, 0, 0, 2);
+ mergeCellsHorizontally(table, 0, 3, 8);
+ // 生成模板第2行(通过合并方式)
+ mergeCellsHorizontally(table, 1, 0, 2);
+ mergeCellsHorizontally(table, 1, 3, 4);
+ mergeCellsHorizontally(table, 1, 5, 6);
+ mergeCellsHorizontally(table, 1, 7, 8);
+ // 生成模板第3行(通过合并方式)
+ mergeCellsHorizontally(table, 2, 0, 2);
+ mergeCellsHorizontally(table, 2, 3, 8);
+ // 生成模板第4行(通过合并方式)
+ mergeCellsHorizontally(table, 3, 0, 2);
+ mergeCellsHorizontally(table, 3, 3, 8);
+ // 生成模板第5行(通过合并方式)
+ mergeCellsHorizontally(table, 4, 0, 2);
+ mergeCellsHorizontally(table, 4, 3, 8);
+ // 生成模板第6行(通过合并方式)
+ mergeCellsHorizontally(table, 5, 0, 2);
+ mergeCellsHorizontally(table, 5, 3, 8);
+ // 生成模板第7行(通过合并方式)
+ mergeCellsHorizontally(table, 6, 0, 2);
+ mergeCellsHorizontally(table, 6, 3, 8);
+ // 生成模板第8行(通过合并方式)
+ mergeCellsHorizontally(table, 7, 0, 8);
+ // 生成模板第9行(通过合并方式)
+ mergeCellsHorizontally(table, 8, 1, 3);
+ mergeCellsHorizontally(table, 8, 4, 5);
+ mergeCellsHorizontally(table, 8, 6, 7);
+ // 生成模板操作步骤
+ for (int stepRowIndex = 0; stepRowIndex < stepNumber; stepRowIndex++) {
+ mergeCellsHorizontally(table, stepRowIndex + 9, 1, 3);
+ mergeCellsHorizontally(table, stepRowIndex + 9, 4, 5);
+ mergeCellsHorizontally(table, stepRowIndex + 9, 6, 7);
+ }
+ //生成模版倒数第三行
+ mergeCellsHorizontally(table, table.getNumberOfRows() - 4, 0, 1);
+ mergeCellsHorizontally(table, table.getNumberOfRows() - 4, 2, 3);
+ mergeCellsHorizontally(table, table.getNumberOfRows() - 4, 4, 5);
+ mergeCellsHorizontally(table, table.getNumberOfRows() - 4, 6, 8);
+ //生成模版倒数第二行
+ mergeCellsHorizontally(table, table.getNumberOfRows() - 3, 0, 1);
+ mergeCellsHorizontally(table, table.getNumberOfRows() - 3, 2, 3);
+ mergeCellsHorizontally(table, table.getNumberOfRows() - 3, 4, 5);
+ mergeCellsHorizontally(table, table.getNumberOfRows() - 3, 6, 8);
+ //生成模版倒数第一行
+ mergeCellsHorizontally(table, table.getNumberOfRows() - 2, 0, 1);
+ mergeCellsHorizontally(table, table.getNumberOfRows() - 2, 2, 8);
+
+ // 在表格中填值
+ fillTbale(table, pendingOutputData);
+
+ }
+
+
+ // 添加表格标题(应该是<章节号>-<表格序号>)
+ private static void addNumberedTableTitle(XWPFDocument doc, String titleText, int tableCount) {
+ int chapter = 4;
+ XWPFParagraph titleParagraph = doc.createParagraph();
+ titleParagraph.setAlignment(ParagraphAlignment.CENTER);
+ XWPFRun run = titleParagraph.createRun();
+ run.setText("表");
+ // 设置标题样式
+ run.setFontFamily("Times New Roman");
+ run.setFontFamily("宋体", XWPFRun.FontCharRange.eastAsia);
+ run.setFontSize(12);
+ CTRPr rPr0 = run.getCTR().isSetRPr() ? run.getCTR().getRPr() : run.getCTR().addNewRPr();
+ rPr0.addNewSz().setVal(BigInteger.valueOf(21));
+ rPr0.addNewSzCs().setVal(BigInteger.valueOf(21));
+
+ // 创建域开始字符
+ XWPFRun startRun = titleParagraph.createRun();
+ CTFldChar startFldChar = startRun.getCTR().addNewFldChar();
+ startFldChar.setFldCharType(STFldCharType.BEGIN);
+
+ // 创建域代码
+ XWPFRun codeRun = titleParagraph.createRun();
+ String domainCode = " SEQ Table \\* ARABIC \\s " + chapter;
+ codeRun.setText(domainCode);
+ // 设置字体大小,可根据需要调整
+ CTRPr rPr1 = codeRun.getCTR().isSetRPr() ? codeRun.getCTR().getRPr() : codeRun.getCTR().addNewRPr();
+ rPr1.addNewSz().setVal(BigInteger.valueOf(21));
+ rPr1.addNewSzCs().setVal(BigInteger.valueOf(21));
+ codeRun.setFontFamily("Times New Roman");
+ codeRun.setFontFamily("宋体", XWPFRun.FontCharRange.eastAsia);
+
+ // 创建域分隔字符
+ XWPFRun sepRun = titleParagraph.createRun();
+ CTFldChar sepFldChar = sepRun.getCTR().addNewFldChar();
+ // 设置字体大小,可根据需要调整
+ CTRPr rPr2 = sepRun.getCTR().isSetRPr() ? sepRun.getCTR().getRPr() : sepRun.getCTR().addNewRPr();
+ rPr2.addNewSz().setVal(BigInteger.valueOf(21));
+ rPr2.addNewSzCs().setVal(BigInteger.valueOf(21));
+ sepFldChar.setFldCharType(STFldCharType.SEPARATE);
+
+ // 创建域结果
+ XWPFRun resultRun = titleParagraph.createRun();
+ resultRun.setText(chapter + " - " + tableCount);
+ // 设置字体大小,可根据需要调整
+ CTRPr rPr3 = resultRun.getCTR().isSetRPr() ? resultRun.getCTR().getRPr() : resultRun.getCTR().addNewRPr();
+ rPr3.addNewSz().setVal(BigInteger.valueOf(21));
+ rPr3.addNewSzCs().setVal(BigInteger.valueOf(21));
+ resultRun.setFontFamily("Times New Roman");
+ resultRun.setFontFamily("宋体", XWPFRun.FontCharRange.eastAsia);
+
+ // 创建域结束字符
+ XWPFRun endRun = titleParagraph.createRun();
+ CTFldChar endFldChar = endRun.getCTR().addNewFldChar();
+ endFldChar.setFldCharType(STFldCharType.END);
+
+ XWPFRun titlerun = titleParagraph.createRun();
+ titlerun.setText(titleText);
+ // 设置字体大小,可根据需要调整
+ CTRPr rPr4 = titlerun.getCTR().isSetRPr() ? titlerun.getCTR().getRPr() : titlerun.getCTR().addNewRPr();
+ rPr4.addNewSz().setVal(BigInteger.valueOf(21));
+ rPr4.addNewSzCs().setVal(BigInteger.valueOf(21));
+ titlerun.setFontFamily("Times New Roman");
+ titlerun.setFontFamily("宋体", XWPFRun.FontCharRange.eastAsia);
+ }
+
+ /**
+ * 创建标题样式
+ * @param document 文档
+ * @param styleId 样式 ID
+ * @param styleName 样式名称
+ * @param fontSize 字体大小
+ */
+ private static void createTitleStyle(XWPFDocument document, String styleId, String styleName, int fontSize, int level, BigInteger numId) {
+ // 获取文档的样式管理器
+ XWPFStyles styles = document.createStyles();
+ // 创建新的样式
+ CTStyle ctStyle = CTStyle.Factory.newInstance();
+ ctStyle.setStyleId(styleId);
+
+ CTString styleNameCT = CTString.Factory.newInstance();
+ styleNameCT.setVal(styleName);
+ ctStyle.setName(styleNameCT);
+
+ // 设置样式类型为段落样式
+ CTDecimalNumber indentNumber = CTDecimalNumber.Factory.newInstance();
+ indentNumber.setVal(BigInteger.valueOf(1));
+ ctStyle.setType(STStyleType.PARAGRAPH);
+
+ // 设置字体和字号
+ CTPPr ppr = ctStyle.addNewPPr();
+ CTSpacing spacing = ppr.addNewSpacing();
+ spacing.setAfter(BigInteger.valueOf(0));
+ spacing.setLineRule(STLineSpacingRule.AUTO);
+
+ CTDecimalNumber outlineLvl = ppr.addNewOutlineLvl();
+ // 根据样式 ID 确定大纲级别
+ if ("Heading1".equals(styleId)) {
+ outlineLvl.setVal(BigInteger.valueOf(0));
+ } else if ("Heading2".equals(styleId)) {
+ outlineLvl.setVal(BigInteger.valueOf(1));
+ } else if ("Heading3".equals(styleId)) {
+ outlineLvl.setVal(BigInteger.valueOf(2));
+ } else if ("Heading4".equals(styleId)) {
+ outlineLvl.setVal(BigInteger.valueOf(3));
+ } else if ("Heading5".equals(styleId)) {
+ outlineLvl.setVal(BigInteger.valueOf(4));
+ }
+
+ // 关联编号
+ CTNumPr numPr = ppr.addNewNumPr();
+ numPr.addNewNumId().setVal(numId);
+ numPr.addNewIlvl().setVal(BigInteger.valueOf(level - 1));
+
+ // 设置字体样式
+ CTRPr rpr = ctStyle.addNewRPr();
+ CTFonts fonts = rpr.addNewRFonts();
+ fonts.setEastAsia("黑体");
+ fonts.setAscii("Times New Roman");
+ fonts.setHAnsi("Times New Roman");
+ rpr.addNewSz().setVal(BigInteger.valueOf(21));
+ rpr.addNewSzCs().setVal(BigInteger.valueOf(21));
+
+ // 将样式添加到样式管理器中
+ styles.addStyle(new XWPFStyle(ctStyle));
+ }
+
+ // 创建多级列表编号模板
+ private static BigInteger createMultiLevelNumbering(XWPFDocument doc) {
+ XWPFNumbering numbering = doc.createNumbering();
+ // 获取或创建 CTNumbering(POI 4.1.2 兼容方式)
+ CTNumbering ctNumbering;
+ try {
+ Field f = XWPFNumbering.class.getDeclaredField("ctNumbering");
+ f.setAccessible(true);
+ ctNumbering = (CTNumbering) f.get(numbering);
+ if (ctNumbering == null) {
+ ctNumbering = CTNumbering.Factory.newInstance();
+ f.set(numbering, ctNumbering);
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("无法初始化编号系统", e);
+ }
+
+ CTAbstractNum abstractNum = CTAbstractNum.Factory.newInstance();
+ abstractNum.setAbstractNumId(BigInteger.ZERO);
+
+ // 级别1样式:1.
+ CTLvl lvl1 = abstractNum.addNewLvl();
+ lvl1.setIlvl(BigInteger.ZERO);
+ lvl1.addNewNumFmt().setVal(STNumberFormat.DECIMAL);
+ lvl1.addNewLvlText().setVal("%1.");
+ lvl1.addNewLvlJc().setVal(STJc.LEFT);
+ lvl1.addNewStart().setVal(BigInteger.ONE);
+
+ // 级别2样式:1.1
+ CTLvl lvl2 = abstractNum.addNewLvl();
+ lvl2.setIlvl(BigInteger.ONE);
+ lvl2.addNewNumFmt().setVal(STNumberFormat.DECIMAL);
+ lvl2.addNewLvlText().setVal("%1.%2.");
+ lvl2.addNewLvlJc().setVal(STJc.LEFT);
+ lvl2.addNewStart().setVal(BigInteger.ONE);
+
+ // 级别3样式:1.1.1
+ CTLvl lvl3 = abstractNum.addNewLvl();
+ lvl3.setIlvl(BigInteger.valueOf(2));
+ lvl3.addNewNumFmt().setVal(STNumberFormat.DECIMAL);
+ lvl3.addNewLvlText().setVal("%1.%2.%3.");
+ lvl3.addNewLvlJc().setVal(STJc.LEFT);
+ lvl3.addNewStart().setVal(BigInteger.ONE);
+
+ // 级别4样式:1.1.1.1
+ CTLvl lvl4 = abstractNum.addNewLvl();
+ lvl4.setIlvl(BigInteger.valueOf(3));
+ lvl4.addNewNumFmt().setVal(STNumberFormat.DECIMAL);
+ lvl4.addNewLvlText().setVal("%1.%2.%3.%4.");
+ lvl4.addNewLvlJc().setVal(STJc.LEFT);
+ lvl4.addNewStart().setVal(BigInteger.ONE);
+
+ // 级别5样式:1.1.1.1.1
+ CTLvl lvl5 = abstractNum.addNewLvl();
+ lvl5.setIlvl(BigInteger.valueOf(4));
+ lvl5.addNewNumFmt().setVal(STNumberFormat.DECIMAL);
+ lvl5.addNewLvlText().setVal("%1.%2.%3.%4.%5.");
+ lvl5.addNewLvlJc().setVal(STJc.LEFT);
+ lvl5.addNewStart().setVal(BigInteger.ONE);
+
+ // 注册到文档
+ ctNumbering.addNewAbstractNum().set(abstractNum);
+
+ CTNum num = ctNumbering.addNewNum();
+ num.setNumId(BigInteger.ONE);
+ num.addNewAbstractNumId().setVal(BigInteger.ZERO);
+
+ return BigInteger.ONE;
+ }
+
+ private static String getFirstPinyin(String text){
+ String regEx = "\\pP|\\pS|\\s+";
+ String reqTitleIdentifier = Pattern.compile(regEx).matcher(text).replaceAll("").trim();
+ reqTitleIdentifier = PinYinStringUtils.toFirstChar(reqTitleIdentifier).toUpperCase();
+ text = text + reqTitleIdentifier;
+ return text;
+ }
+
+ private static void addNumberedHeading(XWPFDocument doc, String text, int level , boolean isGetPinYin) {
+ XWPFParagraph para = doc.createParagraph();
+ para.setStyle("Heading" + level);
+ XWPFRun run = para.createRun();
+ if (isGetPinYin) {
+ text = getFirstPinyin(text);
+ }
+ run.setText(text);
+ }
+
+ // 插入分页符(另起一页)
+ private int insertTable(XWPFDocument doc,List 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);
+
+ XWPFParagraph pageBreak = doc.createParagraph();
+ XWPFRun pageBreakRun = pageBreak.createRun();
+ pageBreakRun.addBreak(BreakType.PAGE);
+
+ }
+ return counts;
+ }
+
+ private void fillMainContent(XWPFDocument document, String text) {
+ XWPFParagraph paragraph = document.createParagraph();
+ paragraph.setAlignment(ParagraphAlignment.LEFT); // 居中
+ // 400 = 2 * 200(一个中文字符大约200单位)
+ paragraph.setIndentationFirstLine(400);
+ // 设置自动换行
+ paragraph.setWordWrapped(true);
+ XWPFRun titleRun = paragraph.createRun();
+ titleRun.setText(text);
+ // 字体大小
+ //设置字体为五号 10.5*2
+ CTRPr rPr = titleRun.getCTR().isSetRPr() ? titleRun.getCTR().getRPr() : titleRun.getCTR().addNewRPr();
+ rPr.addNewSz().setVal(BigInteger.valueOf(21));
+ rPr.addNewSzCs().setVal(BigInteger.valueOf(21));
+ // 字体样式
+ titleRun.setFontFamily("Times New Roman");
+ titleRun.setFontFamily("宋体", XWPFRun.FontCharRange.eastAsia);
+ }
+
+ private void fillFixedContent(XWPFDocument document, Long projectId, List treeSelects){
+ 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);
+
+ //写入第五章内容
+ 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 treeSelects, XWPFDocument document){
+ // 使用栈进行迭代遍历
+ Stack 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 pendingOutputDatas = GetPendingOutputData(projectId, current.getId());
+ tableCounts = insertTable(document, pendingOutputDatas, tableCounts);
+ } else {
+ addNumberedHeading(document, current.getLabel(), current.getLevel() + 1, true);
+ List pendingOutputDatas = GetPendingOutputData(projectId, null);
+ tableCounts = insertTable(document, pendingOutputDatas, tableCounts);
+ }
+ } else{
+ if (current.getId() > 0){
+ addNumberedHeading(document, current.getLabel(), current.getLevel() + 1, true);
+ }
+ }
+ //处理当前节点结束
+
+ // 将子节点压入栈
+ List children = current.getChildren();
+ if (children != null) {
+ for (int i = children.size() - 1; i >= 0; i--) {
+ stack.push(children.get(i));
+ }
+ }
+ }
+ }
+
+ public void generateWord(List treeSelects, Long projectId, HttpServletResponse response) throws IOException {
+ // 创建一个新的 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);
+
+ // 设置响应头
+ 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文件将测试用例导入到数据库
+ * @param projectId 项目id
+ * @param file 导入的docx文件
+ */
+ public void testcaseImport(Long projectId, MultipartFile file) throws IOException {
+ List