mirror of
https://gitee.com/fit2cloud-feizhiyun/MeterSphere.git
synced 2024-11-30 02:58:31 +08:00
feat(测试跟踪): 缺陷管理导入自定义字段校验
--story=1010310 --user=宋昌昌 【测试跟踪】缺陷管理支持通过excel导入导出 https://www.tapd.cn/55049933/s/1295113
This commit is contained in:
parent
820c163cb7
commit
ba22b7c7f7
@ -74,6 +74,7 @@ public class IssueTemplateHeadWriteHandler implements RowWriteHandler, SheetWrit
|
||||
if (BooleanUtils.isTrue(context.getHead())) {
|
||||
sheet = context.getWriteSheetHolder().getSheet();
|
||||
drawingPatriarch = sheet.createDrawingPatriarch();
|
||||
// 设置表头内容
|
||||
headCommentIndexMap.forEach(this::setComment);
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@ -42,10 +43,11 @@ public class IssueExcelListener extends AnalysisEventListener<Map<Integer, Strin
|
||||
|
||||
private Class dataClass;
|
||||
private IssueImportRequest request;
|
||||
private Boolean isThirdPlatform = false;
|
||||
private Boolean isThirdPlatform;
|
||||
private Map<Integer, String> headMap;
|
||||
private List<CustomFieldDao> customFields = new ArrayList<>();
|
||||
private List<CustomFieldDao> customFields;
|
||||
private IssuesService issuesService;
|
||||
private Map<String, String> memberMap;
|
||||
/**
|
||||
* excel表头字段字典值
|
||||
*/
|
||||
@ -66,12 +68,13 @@ public class IssueExcelListener extends AnalysisEventListener<Map<Integer, Strin
|
||||
protected List<IssueExcelData> updateList = new ArrayList<>();
|
||||
protected List<ExcelErrData<IssueExcelData>> errList = new ArrayList<>();
|
||||
|
||||
public IssueExcelListener(IssueImportRequest request, Class clazz, Boolean isThirdPlatform, List<CustomFieldDao> customFields) {
|
||||
public IssueExcelListener(IssueImportRequest request, Class clazz, Boolean isThirdPlatform, List<CustomFieldDao> customFields, Map<String, String> memberMap) {
|
||||
this.request = request;
|
||||
this.dataClass = clazz;
|
||||
this.isThirdPlatform = isThirdPlatform;
|
||||
this.customFields = customFields;
|
||||
this.issuesService = CommonBeanFactory.getBean(IssuesService.class);
|
||||
this.memberMap = memberMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -83,9 +86,9 @@ public class IssueExcelListener extends AnalysisEventListener<Map<Integer, Strin
|
||||
issueExcelData = this.parseDataToModel(data);
|
||||
// EXCEL校验, 如果不是第三方模板则需要校验
|
||||
errMsg = new StringBuilder(!isThirdPlatform ? ExcelValidateHelper.validateEntity(issueExcelData) : StringUtils.EMPTY);
|
||||
//自定义校验规则
|
||||
// 校验自定义字段
|
||||
if (StringUtils.isEmpty(errMsg)) {
|
||||
validate(issueExcelData, errMsg);
|
||||
validateCustomField(issueExcelData, errMsg);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
errMsg = new StringBuilder(Translator.get("parse_data_error"));
|
||||
@ -177,8 +180,29 @@ public class IssueExcelListener extends AnalysisEventListener<Map<Integer, Strin
|
||||
}
|
||||
}
|
||||
|
||||
public void validate(IssueExcelData data, StringBuilder errMsg) {
|
||||
// TODO 校验自定义字段的数据是否合法
|
||||
public void validateCustomField(IssueExcelData data, StringBuilder errMsg) {
|
||||
Map<String, List<CustomFieldDao>> customFieldMap = customFields.stream().collect(Collectors.groupingBy(CustomFieldDao::getName));
|
||||
data.getCustomData().forEach((k, v) -> {
|
||||
List<CustomFieldDao> customFieldDaos = customFieldMap.get(k);
|
||||
if (CollectionUtils.isNotEmpty(customFieldDaos) && customFieldDaos.size() > 0) {
|
||||
CustomFieldDao customFieldDao = customFieldDaos.get(0);
|
||||
String type = customFieldDao.getType();
|
||||
Boolean required = customFieldDao.getRequired();
|
||||
String options = StringUtils.equalsAnyIgnoreCase(type, CustomFieldType.MEMBER.getValue(), CustomFieldType.MULTIPLE_MEMBER.getValue()) ?
|
||||
this.memberMap.toString() : customFieldDao.getOptions();
|
||||
if (required && StringUtils.isEmpty(v.toString())) {
|
||||
errMsg.append(k).append(Translator.get("can_not_be_null")).append(";");
|
||||
} else if (StringUtils.isNotEmpty(v.toString()) && isSelect(type) && !isOptionInclude(v, options)) {
|
||||
errMsg.append(k).append(Translator.get("options_not_exist")).append(";");
|
||||
} else if (StringUtils.isNotEmpty(v.toString()) && isIllegalFormat(type, v.toString())) {
|
||||
errMsg.append(k).append(Translator.get("format_error")).append(";");
|
||||
}
|
||||
} else {
|
||||
if (!exportFieldsContains(k)) {
|
||||
errMsg.append(k).append(Translator.get("excel_field_not_exist")).append(";");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private IssueExcelData parseDataToModel(Map<Integer, String> rowData) {
|
||||
@ -309,7 +333,7 @@ public class IssueExcelListener extends AnalysisEventListener<Map<Integer, Strin
|
||||
CustomFieldType.CHECKBOX.getValue(), CustomFieldType.MULTIPLE_INPUT.getValue(),
|
||||
CustomFieldType.MULTIPLE_MEMBER.getValue(), CustomFieldType.CASCADING_SELECT.getValue())) {
|
||||
if (!v.toString().contains("[")) {
|
||||
v = List.of("\"" + v.toString() + "\"");
|
||||
v = List.of("\"" + v + "\"");
|
||||
}
|
||||
customFieldResourceDTO.setValue(v.toString());
|
||||
} else if (StringUtils.equalsAnyIgnoreCase(type, CustomFieldType.DATE.getValue())) {
|
||||
@ -353,4 +377,51 @@ public class IssueExcelListener extends AnalysisEventListener<Map<Integer, Strin
|
||||
private String getPlatformId(String issueId) {
|
||||
return issuesService.getIssue(issueId).getPlatformId();
|
||||
}
|
||||
|
||||
private Boolean isSelect(String type) {
|
||||
return StringUtils.equalsAnyIgnoreCase(type, CustomFieldType.SELECT.getValue(), CustomFieldType.RADIO.getValue(),
|
||||
CustomFieldType.MULTIPLE_SELECT.getValue(), CustomFieldType.CHECKBOX.getValue(),
|
||||
CustomFieldType.CASCADING_SELECT.getValue(), CustomFieldType.MEMBER.getValue(), CustomFieldType.MULTIPLE_MEMBER.getValue());
|
||||
}
|
||||
|
||||
private Boolean isIllegalFormat(String type, String value) {
|
||||
try {
|
||||
if (StringUtils.equalsAnyIgnoreCase(type, CustomFieldType.DATE.getValue())) {
|
||||
DateUtils.parseDate(value, "yyyy/MM/dd");
|
||||
} else if (StringUtils.equalsAnyIgnoreCase(type, CustomFieldType.DATETIME.getValue())) {
|
||||
DateUtils.parseDate(value);
|
||||
} else if (StringUtils.equalsAnyIgnoreCase(type, CustomFieldType.INT.getValue())) {
|
||||
Integer.parseInt(value);
|
||||
} else if (StringUtils.equalsAnyIgnoreCase(type, CustomFieldType.FLOAT.getValue())) {
|
||||
Float.parseFloat(value);
|
||||
}
|
||||
return Boolean.FALSE;
|
||||
} catch (Exception e) {
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
private Boolean isOptionInclude(Object value, String options) {
|
||||
AtomicReference<Boolean> isInclude = new AtomicReference<>(Boolean.TRUE);
|
||||
if (value instanceof List) {
|
||||
((List<?>) value).forEach(item -> {
|
||||
String s = item.toString().replaceAll("\"", StringUtils.EMPTY);
|
||||
if (!StringUtils.contains(options, s)) {
|
||||
isInclude.set(Boolean.FALSE);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
isInclude.set(StringUtils.contains(options, value.toString()));
|
||||
}
|
||||
return isInclude.get();
|
||||
}
|
||||
|
||||
public Boolean exportFieldsContains(String name) {
|
||||
for (IssueExportHeadField issueExportHeadField : IssueExportHeadField.values()) {
|
||||
if (StringUtils.equals(name, issueExportHeadField.getName())) {
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
}
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
}
|
||||
|
@ -495,13 +495,13 @@ public class IssuesService {
|
||||
issuesRequest.setProjectId(SessionUtils.getCurrentProjectId());
|
||||
List<IssuesDao> issuesDaos = listByWorkspaceId(issuesRequest);
|
||||
if (CollectionUtils.isNotEmpty(issuesDaos)) {
|
||||
issuesDaos.forEach(issuesDao -> {
|
||||
issuesDaos.parallelStream().forEach(issuesDao -> {
|
||||
delete(issuesDao.getId());
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (CollectionUtils.isNotEmpty(request.getBatchDeleteIds())) {
|
||||
request.getBatchDeleteIds().forEach(id -> delete(id));
|
||||
request.getBatchDeleteIds().parallelStream().forEach(id -> delete(id));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -610,17 +610,19 @@ public class IssuesService {
|
||||
if (StringUtils.equalsAnyIgnoreCase(customField.getType(), CustomFieldType.RICH_TEXT.getValue(), CustomFieldType.TEXTAREA.getValue())) {
|
||||
fieldDao.setValue(fieldDao.getTextValue());
|
||||
}
|
||||
if (StringUtils.equalsAnyIgnoreCase(customField.getType(), CustomFieldType.DATE.getValue()) && StringUtils.isNotEmpty(fieldDao.getValue())) {
|
||||
if (StringUtils.equalsAnyIgnoreCase(customField.getType(), CustomFieldType.DATE.getValue()) && StringUtils.isNotEmpty(fieldDao.getValue()) && !StringUtils.equals(fieldDao.getValue(), "null")) {
|
||||
Date date = DateUtils.parseDate(fieldDao.getValue().replaceAll("\"", StringUtils.EMPTY), "yyyy-MM-dd");
|
||||
String format = DateUtils.format(date, "yyyy/MM/dd");
|
||||
fieldDao.setValue("\"" + format + "\"");
|
||||
}
|
||||
if (StringUtils.equalsAnyIgnoreCase(customField.getType(), CustomFieldType.DATETIME.getValue()) && StringUtils.isNotEmpty(fieldDao.getValue())) {
|
||||
if (StringUtils.equalsAnyIgnoreCase(customField.getType(), CustomFieldType.DATETIME.getValue()) && StringUtils.isNotEmpty(fieldDao.getValue()) && !StringUtils.equals(fieldDao.getValue(), "null")) {
|
||||
Date date = null;
|
||||
if (fieldDao.getValue().contains("T") && fieldDao.getValue().length() == 18) {
|
||||
date = DateUtils.parseDate(fieldDao.getValue().replaceAll("\"", StringUtils.EMPTY), "yyyy-MM-dd'T'HH:mm");
|
||||
} else if (fieldDao.getValue().contains("T") && fieldDao.getValue().length() == 21) {
|
||||
date = DateUtils.parseDate(fieldDao.getValue().replaceAll("\"", StringUtils.EMPTY), "yyyy-MM-dd'T'HH:mm:ss");
|
||||
} else if (fieldDao.getValue().contains("T") && fieldDao.getValue().length() > 21) {
|
||||
date = DateUtils.parseDate(fieldDao.getValue().replaceAll("\"", StringUtils.EMPTY).substring(0, 19), "yyyy-MM-dd'T'HH:mm:ss");
|
||||
} else {
|
||||
date = DateUtils.parseDate(fieldDao.getValue().replaceAll("\"", StringUtils.EMPTY));
|
||||
}
|
||||
@ -1194,9 +1196,12 @@ public class IssuesService {
|
||||
|
||||
public void issueImportTemplate(String projectId, HttpServletResponse response) {
|
||||
Map<String, String> userMap = baseUserService.getProjectMemberOption(projectId).stream().collect(Collectors.toMap(User::getId, User::getName));
|
||||
// 获取缺陷模板及自定义字段
|
||||
IssueTemplateDao issueTemplate = getIssueTemplateByProjectId(projectId);
|
||||
List<CustomFieldDao> customFields = Optional.ofNullable(issueTemplate.getCustomFields()).orElse(new ArrayList<>());
|
||||
// 根据自定义字段获取表头
|
||||
List<List<String>> heads = new IssueExcelDataFactory().getIssueExcelDataLocal().getHead(issueTemplate.getIsThirdTemplate(), customFields, null);
|
||||
// 导出空模板, heads->表头, headHandler->表头处理
|
||||
IssueTemplateHeadWriteHandler headHandler = new IssueTemplateHeadWriteHandler(userMap, heads, issueTemplate.getCustomFields());
|
||||
new EasyExcelExporter(new IssueExcelDataFactory().getExcelDataByLocal())
|
||||
.exportByCustomWriteHandler(response, heads, null, Translator.get("issue_import_template_name"),
|
||||
@ -1207,16 +1212,21 @@ public class IssuesService {
|
||||
if (importFile == null) {
|
||||
MSException.throwException(Translator.get("upload_fail"));
|
||||
}
|
||||
Map<String, String> userMap = baseUserService.getProjectMemberOption(request.getProjectId()).stream().collect(Collectors.toMap(User::getId, User::getName));
|
||||
// 获取缺陷模板及自定义字段
|
||||
IssueTemplateDao issueTemplate = getIssueTemplateByProjectId(request.getProjectId());
|
||||
List<CustomFieldDao> customFields = Optional.ofNullable(issueTemplate.getCustomFields()).orElse(new ArrayList<>());
|
||||
// 获取本地EXCEL数据对象
|
||||
Class clazz = new IssueExcelDataFactory().getExcelDataByLocal();
|
||||
IssueExcelListener issueExcelListener = new IssueExcelListener(request, clazz, issueTemplate.getIsThirdTemplate(), customFields);
|
||||
// IssueExcelListener读取file内容
|
||||
IssueExcelListener issueExcelListener = new IssueExcelListener(request, clazz, issueTemplate.getIsThirdTemplate(), customFields, userMap);
|
||||
try {
|
||||
EasyExcelFactory.read(importFile.getInputStream(), issueExcelListener).sheet().doRead();
|
||||
} catch (IOException e) {
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
// 获取错误信息并返回
|
||||
List<ExcelErrData<IssueExcelData>> errList = issueExcelListener.getErrList();
|
||||
ExcelResponse excelResponse = new ExcelResponse();
|
||||
if (CollectionUtils.isNotEmpty(errList)) {
|
||||
@ -1230,24 +1240,33 @@ public class IssuesService {
|
||||
|
||||
public void issueExport(IssueExportRequest request, HttpServletResponse response) {
|
||||
Map<String, String> userMap = baseUserService.getProjectMemberOption(request.getProjectId()).stream().collect(Collectors.toMap(User::getId, User::getName));
|
||||
// 获取缺陷模板及自定义字段
|
||||
IssueTemplateDao issueTemplate = getIssueTemplateByProjectId(request.getProjectId());
|
||||
List<CustomFieldDao> customFields = Optional.ofNullable(issueTemplate.getCustomFields()).orElse(new ArrayList<>());
|
||||
// 根据自定义字段获取表头内容
|
||||
List<List<String>> heads = new IssueExcelDataFactory().getIssueExcelDataLocal().getHead(issueTemplate.getIsThirdTemplate(), customFields, request);
|
||||
// 获取导出缺陷列表
|
||||
List<IssuesDao> exportIssues = getExportIssues(request, issueTemplate.getIsThirdTemplate(), customFields);
|
||||
// 解析issue对象数据->excel对象数据
|
||||
List<IssueExcelData> excelDataList = parseIssueDataToExcelData(exportIssues);
|
||||
// 解析excel对象数据->excel列表数据
|
||||
List<List<Object>> data = parseExcelDataToList(heads, excelDataList);
|
||||
// 导出EXCEL
|
||||
IssueTemplateHeadWriteHandler headHandler = new IssueTemplateHeadWriteHandler(userMap, heads, issueTemplate.getCustomFields());
|
||||
// heads-> 表头内容, data -> 导出EXCEL列表数据, headHandler -> 表头处理
|
||||
new EasyExcelExporter(new IssueExcelDataFactory().getExcelDataByLocal())
|
||||
.exportByCustomWriteHandler(response, heads, data, Translator.get("issue_list_export_excel"),
|
||||
Translator.get("issue_list_export_excel_sheet"), headHandler);
|
||||
}
|
||||
|
||||
public List<IssuesDao> getExportIssues(IssueExportRequest exportRequest, Boolean isThirdTemplate, List<CustomFieldDao> customFields) {
|
||||
// 根据列表条件获取符合缺陷集合
|
||||
IssuesRequest request = new IssuesRequest();
|
||||
request.setProjectId(exportRequest.getProjectId());
|
||||
request.setWorkspaceId(exportRequest.getWorkspaceId());
|
||||
request.setSelectAll(exportRequest.getIsSelectAll());
|
||||
request.setExportIds(exportRequest.getExportIds());
|
||||
// 列表排序
|
||||
request.setOrders(exportRequest.getOrders());
|
||||
request.setOrders(ServiceUtils.getDefaultOrderByField(request.getOrders(), "create_time"));
|
||||
request.getOrders().forEach(order -> {
|
||||
@ -1266,6 +1285,7 @@ public class IssuesService {
|
||||
Map<String, String> planMap = getPlanMap(issues);
|
||||
Map<String, List<IssueCommentDTO>> commentMap = getCommentMap(issues);
|
||||
|
||||
// 设置creator, caseCount, commnet
|
||||
issues.forEach(item -> {
|
||||
User createUser = userMap.get(item.getCreator());
|
||||
if (createUser != null) {
|
||||
@ -1288,6 +1308,7 @@ public class IssuesService {
|
||||
item.setComment(StringUtils.join(comments, ";"));
|
||||
}
|
||||
});
|
||||
// 解析自定义字段
|
||||
buildCustomField(issues, isThirdTemplate, customFields);
|
||||
return issues;
|
||||
}
|
||||
@ -1424,13 +1445,13 @@ public class IssuesService {
|
||||
}
|
||||
|
||||
public void saveImportData(List<IssuesUpdateRequest> issues) {
|
||||
issues.forEach(issue -> {
|
||||
issues.parallelStream().forEach(issue -> {
|
||||
addIssues(issue, null);
|
||||
});
|
||||
}
|
||||
|
||||
public void updateImportData(List<IssuesUpdateRequest> issues) {
|
||||
issues.forEach(issue -> {
|
||||
issues.parallelStream().forEach(issue -> {
|
||||
updateIssues(issue);
|
||||
});
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ int_import_cell_format_comment=cell format: 100001
|
||||
float_import_cell_format_comment=cell format: 24
|
||||
multiple_input_import_cell_format_comment=This field has multiple values. Separate multiple values with commas or semicolons
|
||||
options_tips=(format{key:value}, please fill in the corresponding value)Option value:
|
||||
# issue export
|
||||
# issue import and issue export
|
||||
title==Title
|
||||
description=Description
|
||||
case_count=Case count
|
||||
@ -59,6 +59,10 @@ comment=Comment
|
||||
issue_resource=Issue resource
|
||||
issue_platform=Issue platform
|
||||
create_time=CreateTime
|
||||
can_not_be_null=Can not be null
|
||||
excel_field_not_exist=Not exist
|
||||
options_not_exist=Incorrect option value
|
||||
format_error=Format error
|
||||
#project
|
||||
project_name_is_null=Project name cannot be null
|
||||
project_name_already_exists=The project name already exists
|
||||
|
@ -28,7 +28,7 @@ int_import_cell_format_comment=整型单元格格式为: 100001
|
||||
float_import_cell_format_comment=浮点单元格格式为: 24
|
||||
multiple_input_import_cell_format_comment=该单元格可输入多个值,多个值请用逗号或分号隔开(v1;v2)
|
||||
options_tips=(格式{key:value},请填写对应的value)选项:
|
||||
# issue export
|
||||
# issue import and issue export
|
||||
title=缺陷标题
|
||||
description=缺陷描述
|
||||
case_count=用例数
|
||||
@ -36,6 +36,10 @@ comment=评论
|
||||
issue_resource=缺陷来源
|
||||
issue_platform=缺陷平台
|
||||
create_time=创建时间
|
||||
can_not_be_null=不能为空
|
||||
excel_field_not_exist=不存在该字段
|
||||
options_not_exist=选项值有误
|
||||
format_error=格式有误
|
||||
#project
|
||||
project_name_is_null=项目名称不能为空
|
||||
project_name_already_exists=项目名称已存在
|
||||
|
@ -28,7 +28,7 @@ int_import_cell_format_comment=單元格格式: 100001
|
||||
float_import_cell_format_comment=單元格格式: 24
|
||||
multiple_input_import_cell_format_comment=該單元格可輸入多個值,多個值請用逗號或分號隔開(v1;v2)
|
||||
options_tips=(格式{key:value},請填寫對應value)選項:
|
||||
# issue export
|
||||
# issue import and issue export
|
||||
title=缺陷標題
|
||||
description=缺陷描述
|
||||
case_count=用例數
|
||||
@ -36,6 +36,10 @@ comment=評論
|
||||
issue_resource=缺陷來源
|
||||
issue_platform=缺陷平臺
|
||||
create_time=創建時間
|
||||
can_not_be_null=不能爲空
|
||||
excel_field_not_exist=不存在該字段
|
||||
options_not_exist=選項值有誤
|
||||
format_error=格式有誤
|
||||
#project
|
||||
project_name_is_null=項目名稱不能為空
|
||||
project_name_already_exists=項目名稱已存在
|
||||
|
Loading…
Reference in New Issue
Block a user