diff --git a/backend/src/main/java/io/metersphere/api/controller/APITestController.java b/backend/src/main/java/io/metersphere/api/controller/APITestController.java index 4ddbf28531..6ac534febb 100644 --- a/backend/src/main/java/io/metersphere/api/controller/APITestController.java +++ b/backend/src/main/java/io/metersphere/api/controller/APITestController.java @@ -12,6 +12,7 @@ import io.metersphere.commons.utils.PageUtils; import io.metersphere.commons.utils.Pager; import io.metersphere.commons.utils.SessionUtils; import io.metersphere.controller.request.QueryScheduleRequest; +import io.metersphere.dto.LicenseDTO; import io.metersphere.dto.ScheduleDao; import org.apache.shiro.authz.annotation.Logical; import org.apache.shiro.authz.annotation.RequiresRoles; @@ -19,7 +20,6 @@ import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; - import java.util.List; @RestController @@ -127,4 +127,10 @@ public class APITestController { public List listSchedule(@RequestBody QueryScheduleRequest request) { return apiTestService.listSchedule(request); } + + @GetMapping("/license/valid") + public LicenseDTO valid() { + return apiTestService.validateLicense(); + } + } diff --git a/backend/src/main/java/io/metersphere/api/service/APITestService.java b/backend/src/main/java/io/metersphere/api/service/APITestService.java index ab2d8e2302..83b33c5b37 100644 --- a/backend/src/main/java/io/metersphere/api/service/APITestService.java +++ b/backend/src/main/java/io/metersphere/api/service/APITestService.java @@ -12,7 +12,6 @@ import io.metersphere.api.parse.JmeterDocumentParser; import io.metersphere.base.domain.*; import io.metersphere.base.mapper.ApiTestFileMapper; import io.metersphere.base.mapper.ApiTestMapper; -import io.metersphere.base.mapper.UserMapper; import io.metersphere.base.mapper.ext.ExtApiTestMapper; import io.metersphere.commons.constants.APITestStatus; import io.metersphere.commons.constants.FileType; @@ -21,15 +20,13 @@ import io.metersphere.commons.constants.ScheduleType; import io.metersphere.commons.exception.MSException; import io.metersphere.commons.utils.*; import io.metersphere.controller.request.QueryScheduleRequest; +import io.metersphere.dto.LicenseDTO; import io.metersphere.dto.ScheduleDao; import io.metersphere.i18n.Translator; import io.metersphere.job.sechedule.ApiTestJob; import io.metersphere.notice.service.MailService; import io.metersphere.notice.service.NoticeService; -import io.metersphere.service.FileService; -import io.metersphere.service.QuotaService; -import io.metersphere.service.ScheduleService; -import io.metersphere.service.UserService; +import io.metersphere.service.*; import io.metersphere.track.service.TestCaseService; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; @@ -40,7 +37,6 @@ import org.springframework.util.CollectionUtils; import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; - import java.io.*; import java.util.*; import java.util.stream.Collectors; @@ -441,4 +437,13 @@ public class APITestService { quotaService.checkAPITestQuota(); } } + + public LicenseDTO validateLicense() { + LicenseService licenseService = CommonBeanFactory.getBean(LicenseService.class); + if (licenseService != null) { + return licenseService.valid(); + } + return null; + } + } diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseReviewMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseReviewMapper.xml index 96a18c6767..0dfdc87839 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseReviewMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseReviewMapper.xml @@ -2,12 +2,16 @@ - + select distinct test_case_review.id, test_case_review.name, user.name as creator, test_case_review.status, + test_case_review.create_time, test_case_review.update_time, test_case_review.end_time, + test_case_review.description + from test_case_review, project, test_case_review_project, user test_case_review.id = test_case_review_project.review_id and test_case_review_project.project_id = project.id + and user.id = test_case_review.creator and test_case_review.name like CONCAT('%', #{request.name},'%') @@ -21,7 +25,8 @@ - select distinct test_case_review.* from test_case_review, project, test_case_review_project where test_case_review.id = test_case_review_project.review_id diff --git a/backend/src/main/java/io/metersphere/dto/LicenseDTO.java b/backend/src/main/java/io/metersphere/dto/LicenseDTO.java new file mode 100644 index 0000000000..245f875979 --- /dev/null +++ b/backend/src/main/java/io/metersphere/dto/LicenseDTO.java @@ -0,0 +1,14 @@ +package io.metersphere.dto; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class LicenseDTO implements Serializable { + + private String status; + + private LicenseInfoDTO license; + +} \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/dto/LicenseInfoDTO.java b/backend/src/main/java/io/metersphere/dto/LicenseInfoDTO.java new file mode 100644 index 0000000000..682ebe564e --- /dev/null +++ b/backend/src/main/java/io/metersphere/dto/LicenseInfoDTO.java @@ -0,0 +1,21 @@ +package io.metersphere.dto; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class LicenseInfoDTO implements Serializable { + // 客户名称 + private String corporation; + // 授权截止时间 + private String expired; + //产品名称 + private String product; + //产品版本 + private String edition; + //icense版本 + private String licenseVersion; + //授权数量 + private int licenseCount; +} diff --git a/backend/src/main/java/io/metersphere/service/LicenseService.java b/backend/src/main/java/io/metersphere/service/LicenseService.java new file mode 100644 index 0000000000..8636d4ae0e --- /dev/null +++ b/backend/src/main/java/io/metersphere/service/LicenseService.java @@ -0,0 +1,10 @@ +package io.metersphere.service; + +import io.metersphere.dto.LicenseDTO; + +public interface LicenseService { + + public LicenseDTO valid(); + + public LicenseDTO addValidLicense(String reqLicenseCode); +} diff --git a/backend/src/main/java/io/metersphere/track/service/TestCaseService.java b/backend/src/main/java/io/metersphere/track/service/TestCaseService.java index 2239515a26..07bb336bad 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestCaseService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestCaseService.java @@ -27,7 +27,7 @@ import io.metersphere.i18n.Translator; import io.metersphere.track.dto.TestCaseDTO; import io.metersphere.track.request.testcase.QueryTestCaseRequest; import io.metersphere.track.request.testcase.TestCaseBatchRequest; -import io.metersphere.xmind.XmindToTestCaseParser; +import io.metersphere.xmind.XmindCaseParser; import org.apache.commons.lang3.StringUtils; import org.apache.ibatis.session.ExecutorType; import org.apache.ibatis.session.SqlSession; @@ -273,13 +273,18 @@ public class TestCaseService { if (multipartFile.getOriginalFilename().endsWith(".xmind")) { try { errList = new ArrayList<>(); - String processLog = new XmindToTestCaseParser(this, userId, projectId, testCaseNames).importXmind(multipartFile); + XmindCaseParser xmindParser = new XmindCaseParser(this, userId, projectId, testCaseNames); + String processLog = xmindParser.parse(multipartFile); if (!StringUtils.isEmpty(processLog)) { excelResponse.setSuccess(false); - ExcelErrData excelErrData = new ExcelErrData(null, 1, Translator.get("upload_fail")+":"+ processLog); + ExcelErrData excelErrData = new ExcelErrData(null, 1, Translator.get("upload_fail") + ":" + processLog); errList.add(excelErrData); excelResponse.setErrList(errList); } else { + if (!xmindParser.getTestCase().isEmpty()) { + this.saveImportData(xmindParser.getTestCase(), projectId); + xmindParser.clear(); + } excelResponse.setSuccess(true); } } catch (Exception e) { @@ -345,7 +350,7 @@ public class TestCaseService { // 发送给客户端的数据 byte[] buff = new byte[1024]; try (OutputStream outputStream = res.getOutputStream(); - BufferedInputStream bis = new BufferedInputStream(TestCaseService.class.getResourceAsStream("/io/metersphere/xmind/template/testcase.xml"));) { + BufferedInputStream bis = new BufferedInputStream(TestCaseService.class.getResourceAsStream("/io/metersphere/xmind/template/xmind.xml"));) { int i = bis.read(buff); while (i != -1) { outputStream.write(buff, 0, buff.length); diff --git a/backend/src/main/java/io/metersphere/xmind/XmindToTestCaseParser.java b/backend/src/main/java/io/metersphere/xmind/XmindCaseParser.java similarity index 71% rename from backend/src/main/java/io/metersphere/xmind/XmindToTestCaseParser.java rename to backend/src/main/java/io/metersphere/xmind/XmindCaseParser.java index 699948c904..eef17be4a7 100644 --- a/backend/src/main/java/io/metersphere/xmind/XmindToTestCaseParser.java +++ b/backend/src/main/java/io/metersphere/xmind/XmindCaseParser.java @@ -1,6 +1,5 @@ package io.metersphere.xmind; -import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import io.metersphere.base.domain.TestCaseWithBLOBs; @@ -11,15 +10,11 @@ import io.metersphere.excel.domain.TestCaseExcelData; import io.metersphere.i18n.Translator; import io.metersphere.track.service.TestCaseService; import io.metersphere.xmind.parser.XmindParser; -import io.metersphere.xmind.parser.domain.Attached; -import io.metersphere.xmind.parser.domain.JsonRootBean; +import io.metersphere.xmind.parser.pojo.Attached; +import io.metersphere.xmind.parser.pojo.JsonRootBean; import org.springframework.util.StringUtils; import org.springframework.web.multipart.MultipartFile; -import java.io.File; -import java.io.FileOutputStream; -import java.io.InputStream; -import java.io.OutputStream; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -27,35 +22,48 @@ import java.util.regex.Pattern; /** * 数据转换 * 1 解析Xmind文件 XmindParser.parseJson - * 2 解析后的JSON 转成测试用例 + * 2 解析后的JSON this.parse 转成测试用例 */ -public class XmindToTestCaseParser { +public class XmindCaseParser { private TestCaseService testCaseService; private String maintainer; private String projectId; private StringBuffer process; // 过程校验记录 + // 已存在用例名称 private Set testCaseNames; - public XmindToTestCaseParser(TestCaseService testCaseService, String userId, String projectId, Set testCaseNames) { + // 案例详情重写了hashCode方法去重用 + private List testCases; + + // 用于重复对比 + private List compartDatas; + + public XmindCaseParser(TestCaseService testCaseService, String userId, String projectId, Set testCaseNames) { this.testCaseService = testCaseService; this.maintainer = userId; this.projectId = projectId; this.testCaseNames = testCaseNames; - testCaseWithBLOBs = new LinkedList<>(); - xmindDataList = new ArrayList<>(); + testCases = new LinkedList<>(); + compartDatas = new ArrayList<>(); process = new StringBuffer(); } - // 案例详情 - private List testCaseWithBLOBs; - // 用于重复对比 - protected List xmindDataList; + // 这里清理是为了 加快jvm 回收 + public void clear() { + compartDatas.clear(); + testCases.clear(); + testCaseNames.clear(); + } + + public List getTestCase() { + return this.testCases; + } // 递归处理案例数据 - private void makeXmind(StringBuffer processBuffer, Attached parent, int level, String nodePath, List attacheds) { + private void recursion(StringBuffer processBuffer, Attached parent, int level, String nodePath, List attacheds) { for (Attached item : attacheds) { - if (isBlack(item.getTitle(), "(?:tc:|tc:|tc)")) { // 用例 + if (isAvailable(item.getTitle(), "(?:tc:|tc:|tc)")) { // 用例 item.setParent(parent); this.newTestCase(item.getTitle(), parent.getPath(), item.getChildren() != null ? item.getChildren().getAttached() : null); } else { @@ -63,14 +71,13 @@ public class XmindToTestCaseParser { item.setPath(nodePath); if (item.getChildren() != null && !item.getChildren().getAttached().isEmpty()) { item.setParent(parent); - makeXmind(processBuffer, item, level + 1, nodePath, item.getChildren().getAttached()); + recursion(processBuffer, item, level + 1, nodePath, item.getChildren().getAttached()); } } } } - private boolean isBlack(String str, String regex) { - // regex = "(?:tc:|tc:)" + private boolean isAvailable(String str, String regex) { if (StringUtils.isEmpty(str) || StringUtils.isEmpty(regex)) return false; Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE); @@ -88,7 +95,7 @@ public class XmindToTestCaseParser { } // 获取步骤数据 - public String getSteps(List attacheds) { + private String getSteps(List attacheds) { JSONArray jsonArray = new JSONArray(); for (int i = 0; i < attacheds.size(); i++) { // 保持插入顺序,判断用例是否有相同的steps @@ -148,9 +155,9 @@ public class XmindToTestCaseParser { List steps = new LinkedList<>(); if (attacheds != null && !attacheds.isEmpty()) { attacheds.forEach(item -> { - if (isBlack(item.getTitle(), "(?:pc:|pc:)")) { + if (isAvailable(item.getTitle(), "(?:pc:|pc:)")) { testCase.setPrerequisite(replace(item.getTitle(), "(?:pc:|pc:)")); - } else if (isBlack(item.getTitle(), "(?:rc:|rc:)")) { + } else if (isAvailable(item.getTitle(), "(?:rc:|rc:)")) { testCase.setRemark(replace(item.getTitle(), "(?:rc:|rc:)")); } else { steps.add(item); @@ -171,49 +178,19 @@ public class XmindToTestCaseParser { } TestCaseExcelData compartData = new TestCaseExcelData(); BeanUtils.copyBean(compartData, testCase); - if (xmindDataList.contains(compartData)) { + if (compartDatas.contains(compartData)) { process.append(Translator.get("test_case_already_exists_excel") + ":" + testCase.getName() + "; "); } else if (validate(testCase)) { testCase.setId(UUID.randomUUID().toString()); testCase.setCreateTime(System.currentTimeMillis()); testCase.setUpdateTime(System.currentTimeMillis()); - testCaseWithBLOBs.add(testCase); + testCases.add(testCase); } - xmindDataList.add(compartData); + compartDatas.add(compartData); } - //获取流文件 - private static void inputStreamToFile(InputStream ins, File file) { - try (OutputStream os = new FileOutputStream(file);) { - int bytesRead = 0; - byte[] buffer = new byte[8192]; - while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) { - os.write(buffer, 0, bytesRead); - } - } catch (Exception e) { - LogUtil.error(e.getMessage()); - } - } - - /** - * MultipartFile 转 File - * - * @param file - * @throws Exception - */ - private File multipartFileToFile(MultipartFile file) throws Exception { - if (file != null && file.getSize() > 0) { - try (InputStream ins = file.getInputStream();) { - File toFile = new File(file.getOriginalFilename()); - inputStreamToFile(ins, toFile); - return toFile; - } - } - return null; - } - - - public boolean validate(TestCaseWithBLOBs data) { + // 验证合法性 + private boolean validate(TestCaseWithBLOBs data) { String nodePath = data.getNodePath(); StringBuilder stringBuilder = new StringBuilder(); @@ -237,8 +214,6 @@ public class XmindToTestCaseParser { if (testCaseNames.contains(data.getName())) { boolean dbExist = testCaseService.exist(data); - boolean excelExist = false; - if (dbExist) { // db exist stringBuilder.append(Translator.get("test_case_already_exists_excel") + ":" + data.getName() + "; "); @@ -255,49 +230,29 @@ public class XmindToTestCaseParser { } // 导入思维导图处理 - public String importXmind(MultipartFile multipartFile) { + public String parse(MultipartFile multipartFile) { StringBuffer processBuffer = new StringBuffer(); - File file = null; try { - file = multipartFileToFile(multipartFile); - if (file == null || !file.exists()) - return Translator.get("incorrect_format"); - // 获取思维导图内容 - String content = XmindParser.parseJson(file); - if (StringUtils.isEmpty(content) || content.split("(?:tc:|tc:|TC:|TC:|tc|TC)").length == 1) { - return Translator.get("import_xmind_not_found"); - } - if (!StringUtils.isEmpty(content) && content.split("(?:tc:|tc:|TC:|TC:|tc|TC)").length > 500) { - return Translator.get("import_xmind_count_error"); - } - JsonRootBean root = JSON.parseObject(content, JsonRootBean.class); - + JsonRootBean root = XmindParser.parseObject(multipartFile); if (root != null && root.getRootTopic() != null && root.getRootTopic().getChildren() != null) { // 判断是模块还是用例 for (Attached item : root.getRootTopic().getChildren().getAttached()) { - if (isBlack(item.getTitle(), "(?:tc:|tc:|tc)")) { // 用例 + if (isAvailable(item.getTitle(), "(?:tc:|tc:|tc)")) { // 用例 return replace(item.getTitle(), "(?:tc:|tc:|tc)") + ":" + Translator.get("test_case_create_module_fail"); } else { item.setPath(item.getTitle()); if (item.getChildren() != null && !item.getChildren().getAttached().isEmpty()) { item.setPath(item.getTitle()); - makeXmind(processBuffer, item, 1, item.getPath(), item.getChildren().getAttached()); + recursion(processBuffer, item, 1, item.getPath(), item.getChildren().getAttached()); } } } } - if (StringUtils.isEmpty(process.toString()) && !testCaseWithBLOBs.isEmpty()) { - testCaseService.saveImportData(testCaseWithBLOBs, projectId); - } } catch (Exception ex) { processBuffer.append(Translator.get("incorrect_format")); LogUtil.error(ex.getMessage()); - ex.printStackTrace(); - } finally { - if (file != null) - file.delete(); - testCaseWithBLOBs.clear(); + return ex.getMessage(); } return process.toString(); } diff --git a/backend/src/main/java/io/metersphere/xmind/parser/XmindParser.java b/backend/src/main/java/io/metersphere/xmind/parser/XmindParser.java index 44e1cd5055..d61a28a9d7 100644 --- a/backend/src/main/java/io/metersphere/xmind/parser/XmindParser.java +++ b/backend/src/main/java/io/metersphere/xmind/parser/XmindParser.java @@ -1,9 +1,14 @@ package io.metersphere.xmind.parser; import com.alibaba.fastjson.JSON; -import io.metersphere.xmind.parser.domain.JsonRootBean; +import io.metersphere.commons.exception.MSException; +import io.metersphere.i18n.Translator; +import io.metersphere.xmind.parser.pojo.JsonRootBean; +import io.metersphere.xmind.utils.FileUtil; import org.apache.commons.compress.archivers.ArchiveException; import org.dom4j.DocumentException; +import org.springframework.util.StringUtils; +import org.springframework.web.multipart.MultipartFile; import java.io.File; import java.io.IOException; @@ -16,101 +21,98 @@ import java.util.Objects; * @Description 解析主体 */ public class XmindParser { - public static final String xmindZenJson = "content.json"; - public static final String xmindLegacyContent = "content.xml"; - public static final String xmindLegacyComments = "comments.xml"; + public static final String xmindZenJson = "content.json"; + public static final String xmindLegacyContent = "content.xml"; + public static final String xmindLegacyComments = "comments.xml"; - /** - * 解析脑图文件,返回content整合后的内容 - * - * @param file - * @return - * @throws IOException - * @throws ArchiveException - * @throws DocumentException - */ - public static String parseJson(File file) throws IOException, ArchiveException, DocumentException { - String res = ZipUtils.extract(file); + /** + * 解析脑图文件,返回content整合后的内容 + * + * @param multipartFile + * @return + * @throws IOException + * @throws ArchiveException + * @throws DocumentException + */ + public static String parseJson(MultipartFile multipartFile) throws IOException, ArchiveException, DocumentException { - String content = null; - if (isXmindZen(res, file)) { - content = getXmindZenContent(file, res); - } else { - content = getXmindLegacyContent(file, res); - } + File file = FileUtil.multipartFileToFile(multipartFile); + if (file == null || !file.exists()) + MSException.throwException(Translator.get("incorrect_format")); - // 删除生成的文件夹 - File dir = new File(res); - boolean flag = deleteDir(dir); - if (flag) { - // do something - } - JsonRootBean jsonRootBean = JSON.parseObject(content, JsonRootBean.class); - return (JSON.toJSONString(jsonRootBean, false)); - } + String res = ZipUtils.extract(file); + String content = null; + if (isXmindZen(res, file)) { + content = getXmindZenContent(file, res); + } else { + content = getXmindLegacyContent(file, res); + } - public static JsonRootBean parseObject(File file) throws DocumentException, ArchiveException, IOException { - String content = parseJson(file); - JsonRootBean jsonRootBean = JSON.parseObject(content, JsonRootBean.class); - return jsonRootBean; - } + // 删除生成的文件夹 + File dir = new File(res); + FileUtil.deleteDir(dir); + JsonRootBean jsonRootBean = JSON.parseObject(content, JsonRootBean.class); + // 删除零时文件 + if (file != null) + file.delete(); + String json = (JSON.toJSONString(jsonRootBean, false)); - public static boolean deleteDir(File dir) { - if (dir.isDirectory()) { - String[] children = dir.list(); - // 递归删除目录中的子目录下 - for (int i = 0; i < children.length; i++) { - boolean success = deleteDir(new File(dir, children[i])); - if (!success) { - return false; - } - } - } - // 目录此时为空,可以删除 - return dir.delete(); - } + if (StringUtils.isEmpty(content) || content.split("(?:tc:|tc:|TC:|TC:|tc|TC)").length == 1) { + MSException.throwException(Translator.get("import_xmind_not_found")); + } + if (!StringUtils.isEmpty(content) && content.split("(?:tc:|tc:|TC:|TC:|tc|TC)").length > 500) { + MSException.throwException(Translator.get("import_xmind_count_error")); + } + return json; + } - /** - * @return - */ - public static String getXmindZenContent(File file, String extractFileDir) - throws IOException, ArchiveException { - List keys = new ArrayList<>(); - keys.add(xmindZenJson); - Map map = ZipUtils.getContents(keys, file, extractFileDir); - String content = map.get(xmindZenJson); - content = XmindZen.getContent(content); - return content; - } + public static JsonRootBean parseObject(MultipartFile multipartFile) throws DocumentException, ArchiveException, IOException { + String content = parseJson(multipartFile); + JsonRootBean jsonRootBean = JSON.parseObject(content, JsonRootBean.class); + return jsonRootBean; + } - /** - * @return - */ - public static String getXmindLegacyContent(File file, String extractFileDir) - throws IOException, ArchiveException, DocumentException { - List keys = new ArrayList<>(); - keys.add(xmindLegacyContent); - keys.add(xmindLegacyComments); - Map map = ZipUtils.getContents(keys, file, extractFileDir); + /** + * @return + */ + public static String getXmindZenContent(File file, String extractFileDir) + throws IOException, ArchiveException { + List keys = new ArrayList<>(); + keys.add(xmindZenJson); + Map map = ZipUtils.getContents(keys, file, extractFileDir); + String content = map.get(xmindZenJson); + content = XmindZen.getContent(content); + return content; + } - String contentXml = map.get(xmindLegacyContent); - String commentsXml = map.get(xmindLegacyComments); - String xmlContent = XmindLegacy.getContent(contentXml, commentsXml); + /** + * @return + */ + public static String getXmindLegacyContent(File file, String extractFileDir) + throws IOException, ArchiveException, DocumentException { + List keys = new ArrayList<>(); + keys.add(xmindLegacyContent); + keys.add(xmindLegacyComments); + Map map = ZipUtils.getContents(keys, file, extractFileDir); - return xmlContent; - } + String contentXml = map.get(xmindLegacyContent); + String commentsXml = map.get(xmindLegacyComments); + String xmlContent = XmindLegacy.getContent(contentXml, commentsXml); - private static boolean isXmindZen(String res, File file) throws IOException, ArchiveException { - // 解压 - File parent = new File(res); - if (parent.isDirectory()) { - String[] files = parent.list(new ZipUtils.FileFilter()); - for (int i = 0; i < Objects.requireNonNull(files).length; i++) { - if (files[i].equals(xmindZenJson)) { - return true; - } - } - } - return false; - } + return xmlContent; + } + + private static boolean isXmindZen(String res, File file) throws IOException, ArchiveException { + // 解压 + File parent = new File(res); + if (parent.isDirectory()) { + String[] files = parent.list(new ZipUtils.FileFilter()); + for (int i = 0; i < Objects.requireNonNull(files).length; i++) { + if (files[i].equals(xmindZenJson)) { + return true; + } + } + } + return false; + } } diff --git a/backend/src/main/java/io/metersphere/xmind/parser/domain/Attached.java b/backend/src/main/java/io/metersphere/xmind/parser/pojo/Attached.java similarity index 84% rename from backend/src/main/java/io/metersphere/xmind/parser/domain/Attached.java rename to backend/src/main/java/io/metersphere/xmind/parser/pojo/Attached.java index df7932a26d..5781da5500 100755 --- a/backend/src/main/java/io/metersphere/xmind/parser/domain/Attached.java +++ b/backend/src/main/java/io/metersphere/xmind/parser/pojo/Attached.java @@ -1,5 +1,5 @@ -package io.metersphere.xmind.parser.domain; +package io.metersphere.xmind.parser.pojo; import lombok.Data; diff --git a/backend/src/main/java/io/metersphere/xmind/parser/domain/Children.java b/backend/src/main/java/io/metersphere/xmind/parser/pojo/Children.java similarity index 71% rename from backend/src/main/java/io/metersphere/xmind/parser/domain/Children.java rename to backend/src/main/java/io/metersphere/xmind/parser/pojo/Children.java index 761a5fd9ce..c24b4cdd3d 100755 --- a/backend/src/main/java/io/metersphere/xmind/parser/domain/Children.java +++ b/backend/src/main/java/io/metersphere/xmind/parser/pojo/Children.java @@ -1,4 +1,4 @@ -package io.metersphere.xmind.parser.domain; +package io.metersphere.xmind.parser.pojo; import lombok.Data; diff --git a/backend/src/main/java/io/metersphere/xmind/parser/domain/Comments.java b/backend/src/main/java/io/metersphere/xmind/parser/pojo/Comments.java similarity index 74% rename from backend/src/main/java/io/metersphere/xmind/parser/domain/Comments.java rename to backend/src/main/java/io/metersphere/xmind/parser/pojo/Comments.java index e96dda1065..a5204e1f97 100755 --- a/backend/src/main/java/io/metersphere/xmind/parser/domain/Comments.java +++ b/backend/src/main/java/io/metersphere/xmind/parser/pojo/Comments.java @@ -1,4 +1,4 @@ -package io.metersphere.xmind.parser.domain; +package io.metersphere.xmind.parser.pojo; import lombok.Data; diff --git a/backend/src/main/java/io/metersphere/xmind/parser/domain/JsonRootBean.java b/backend/src/main/java/io/metersphere/xmind/parser/pojo/JsonRootBean.java similarity index 74% rename from backend/src/main/java/io/metersphere/xmind/parser/domain/JsonRootBean.java rename to backend/src/main/java/io/metersphere/xmind/parser/pojo/JsonRootBean.java index f69dc6d9d4..93d3bf0008 100755 --- a/backend/src/main/java/io/metersphere/xmind/parser/domain/JsonRootBean.java +++ b/backend/src/main/java/io/metersphere/xmind/parser/pojo/JsonRootBean.java @@ -1,4 +1,4 @@ -package io.metersphere.xmind.parser.domain; +package io.metersphere.xmind.parser.pojo; import lombok.Data; diff --git a/backend/src/main/java/io/metersphere/xmind/parser/domain/Notes.java b/backend/src/main/java/io/metersphere/xmind/parser/pojo/Notes.java similarity index 62% rename from backend/src/main/java/io/metersphere/xmind/parser/domain/Notes.java rename to backend/src/main/java/io/metersphere/xmind/parser/pojo/Notes.java index 882ee5682d..d3bfe93e24 100755 --- a/backend/src/main/java/io/metersphere/xmind/parser/domain/Notes.java +++ b/backend/src/main/java/io/metersphere/xmind/parser/pojo/Notes.java @@ -1,4 +1,4 @@ -package io.metersphere.xmind.parser.domain; +package io.metersphere.xmind.parser.pojo; import lombok.Data; diff --git a/backend/src/main/java/io/metersphere/xmind/parser/domain/RootTopic.java b/backend/src/main/java/io/metersphere/xmind/parser/pojo/RootTopic.java similarity index 82% rename from backend/src/main/java/io/metersphere/xmind/parser/domain/RootTopic.java rename to backend/src/main/java/io/metersphere/xmind/parser/pojo/RootTopic.java index 74deb34695..eebaa0b43b 100755 --- a/backend/src/main/java/io/metersphere/xmind/parser/domain/RootTopic.java +++ b/backend/src/main/java/io/metersphere/xmind/parser/pojo/RootTopic.java @@ -1,4 +1,4 @@ -package io.metersphere.xmind.parser.domain; +package io.metersphere.xmind.parser.pojo; import lombok.Data; diff --git a/backend/src/main/java/io/metersphere/xmind/template/testcase.xml b/backend/src/main/java/io/metersphere/xmind/template/xmind.xml similarity index 100% rename from backend/src/main/java/io/metersphere/xmind/template/testcase.xml rename to backend/src/main/java/io/metersphere/xmind/template/xmind.xml diff --git a/backend/src/main/java/io/metersphere/xmind/utils/FileUtil.java b/backend/src/main/java/io/metersphere/xmind/utils/FileUtil.java new file mode 100644 index 0000000000..60ad9cf53f --- /dev/null +++ b/backend/src/main/java/io/metersphere/xmind/utils/FileUtil.java @@ -0,0 +1,61 @@ +package io.metersphere.xmind.utils; + +import io.metersphere.commons.utils.LogUtil; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; + +public class FileUtil { + + //获取流文件 + private static void inputStreamToFile(InputStream ins, File file) { + try (OutputStream os = new FileOutputStream(file);) { + int bytesRead = 0; + byte[] buffer = new byte[8192]; + while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) { + os.write(buffer, 0, bytesRead); + } + } catch (Exception e) { + LogUtil.error(e.getMessage()); + } + } + + /** + * MultipartFile 转 File + * + * @param file + * @throws Exception + */ + public static File multipartFileToFile(MultipartFile file) { + if (file != null && file.getSize() > 0) { + try (InputStream ins = file.getInputStream();) { + File toFile = new File(file.getOriginalFilename()); + inputStreamToFile(ins, toFile); + return toFile; + } catch (Exception e) { + LogUtil.error(e.getMessage()); + } + } + return null; + } + + public static boolean deleteDir(File dir) { + if (dir.isDirectory()) { + String[] children = dir.list(); + // 递归删除目录中的子目录下 + for (int i = 0; i < children.length; i++) { + boolean success = deleteDir(new File(dir, children[i])); + if (!success) { + return false; + } + } + } + // 目录此时为空,可以删除 + return dir.delete(); + } + + +} diff --git a/frontend/src/business/App.vue b/frontend/src/business/App.vue index 1fb604f01a..266b73605e 100644 --- a/frontend/src/business/App.vue +++ b/frontend/src/business/App.vue @@ -2,7 +2,7 @@ -
License has expired since +
License has expired since {{(validData!= undefined && validData.license!= undefined) ? validData.license.expired:''}},please update license.
@@ -40,15 +40,7 @@ export default { name: 'app', data() { - let xpack = false; - Setting.children.forEach(child => { - if (child.path === "license") { - xpack = true; - return; - } - }) return { - valid: xpack, validData: {}, auth: false } @@ -67,13 +59,14 @@ }); }, beforeMount() { - if (this.valid === true) { - // 验证license - this.result = this.$get("/license/valid", response => { + // 验证license + this.result = this.$get("/api/license/valid", response => { + let data = response.data; + if (data != undefined && data != null) { this.validData = response.data; saveLicense(response.data); - }); - } + } + }); }, components: {MsLanguageSwitch, MsUser, MsView, MsTopMenus, MsHeaderOrgWs}, methods: {} diff --git a/frontend/src/business/components/api/test/components/ApiEnvironmentConfig.vue b/frontend/src/business/components/api/test/components/ApiEnvironmentConfig.vue index 86bd3aa750..bd70cc938e 100644 --- a/frontend/src/business/components/api/test/components/ApiEnvironmentConfig.vue +++ b/frontend/src/business/components/api/test/components/ApiEnvironmentConfig.vue @@ -20,7 +20,7 @@ import MsMainContainer from "../../../common/components/MsMainContainer"; import MsAsideItem from "../../../common/components/MsAsideItem"; import EnvironmentEdit from "./environment/EnvironmentEdit"; - import {listenGoBack, removeGoBackListener} from "../../../../../common/js/utils"; + import {deepClone, listenGoBack, removeGoBackListener} from "../../../../../common/js/utils"; import {Environment, parseEnvironment} from "../model/EnvironmentModel"; export default { @@ -68,12 +68,13 @@ } }, copyEnvironment(environment) { + this.currentEnvironment = environment; if (!environment.id) { this.$warning(this.$t('commons.please_save')); return; } let newEnvironment = {}; - Object.assign(newEnvironment, environment); + newEnvironment = new Environment(environment); newEnvironment.id = null; newEnvironment.name = this.getNoRepeatName(newEnvironment.name); if (!this.validateEnvironment(newEnvironment)) { @@ -84,11 +85,7 @@ this.$refs.environmentItems.itemSelected(this.environments.length - 1, newEnvironment); }, validateEnvironment(environment) { - if (!environment.name || !!environment.name && environment.name.length > 64) { - this.$error(this.$t('commons.input_limit', [1, 64])); - return false; - } - if (!this.$refs.environmentEdit.validateSocket(environment.socket)) { + if (!this.$refs.environmentEdit.validate()) { this.$error(this.$t('commons.formatErr')); return false; } diff --git a/frontend/src/business/components/api/test/components/environment/EnvironmentEdit.vue b/frontend/src/business/components/api/test/components/environment/EnvironmentEdit.vue index 6f8ac770a0..ccf5c9ed79 100644 --- a/frontend/src/business/components/api/test/components/environment/EnvironmentEdit.vue +++ b/frontend/src/business/components/api/test/components/environment/EnvironmentEdit.vue @@ -79,6 +79,17 @@ } }); }, + validate() { + let isValidate = false; + this.$refs['environment'].validate((valid) => { + if (valid && this.$refs.commonConfig.validate() && this.$refs.httpConfig.validate()) { + isValidate = true; + } else { + isValidate = false; + } + }); + return isValidate; + }, _save(environment) { let param = this.buildParam(environment); let url = '/api/environment/add'; diff --git a/frontend/src/business/components/api/test/model/EnvironmentModel.js b/frontend/src/business/components/api/test/model/EnvironmentModel.js index aa4207d246..0f26c89ba5 100644 --- a/frontend/src/business/components/api/test/model/EnvironmentModel.js +++ b/frontend/src/business/components/api/test/model/EnvironmentModel.js @@ -8,13 +8,14 @@ export class Environment extends BaseConfig { this.projectId = undefined; this.name = undefined; this.id = undefined; - this.config = options.config || new Config(); + this.config = undefined; this.set(options); this.sets({}, options); } initOptions(options = {}) { + this.config = new Config(options.config); return options; } } @@ -22,14 +23,16 @@ export class Environment extends BaseConfig { export class Config extends BaseConfig { constructor(options = {}) { super(); - this.commonConfig = options.commonConfig || new CommonConfig(); - this.httpConfig = options.httpConfig || new HttpConfig(); + this.commonConfig = undefined; + this.httpConfig = undefined; this.databaseConfigs = []; this.set(options); this.sets({databaseConfigs: DatabaseConfig}, options); } initOptions(options = {}) { + this.commonConfig = new CommonConfig(options.commonConfig); + this.httpConfig = new HttpConfig(options.httpConfig); options.databaseConfigs = options.databaseConfigs || []; return options; } diff --git a/frontend/src/business/components/track/head/TrackHeaderMenus.vue b/frontend/src/business/components/track/head/TrackHeaderMenus.vue index a49833f7d2..341bb490bd 100644 --- a/frontend/src/business/components/track/head/TrackHeaderMenus.vue +++ b/frontend/src/business/components/track/head/TrackHeaderMenus.vue @@ -32,12 +32,12 @@ - + - + @@ -94,7 +94,7 @@ export default { } }, reviewRecent: { - title: "最近的评审", + title: this.$t('test_track.recent_review'), url: "/test/case/review/recent/5", index: function (item) { return '/track/review/view/' + item.id; diff --git a/frontend/src/business/components/track/review/components/TestCaseReviewEdit.vue b/frontend/src/business/components/track/review/components/TestCaseReviewEdit.vue index ecb6fa1811..9c411e16e0 100644 --- a/frontend/src/business/components/track/review/components/TestCaseReviewEdit.vue +++ b/frontend/src/business/components/track/review/components/TestCaseReviewEdit.vue @@ -3,7 +3,7 @@
@@ -23,10 +23,10 @@ - + - + @@ -62,7 +62,7 @@ - + @@ -84,7 +84,7 @@ - + diff --git a/frontend/src/business/components/track/review/components/TestCaseReviewList.vue b/frontend/src/business/components/track/review/components/TestCaseReviewList.vue index 6ac142939a..87651c0f24 100644 --- a/frontend/src/business/components/track/review/components/TestCaseReviewList.vue +++ b/frontend/src/business/components/track/review/components/TestCaseReviewList.vue @@ -3,8 +3,8 @@