This commit is contained in:
q4speed 2020-09-22 17:13:13 +08:00
commit 8bdfeced41
16 changed files with 314 additions and 117 deletions

View File

@ -16,6 +16,8 @@ public interface ExtUserMapper {
List<User> searchUser(String condition);
List<String> queryEmails(String[] names);
List<String> queryEmails(String[] names);
List<String> queryEmailByIds(List<String> userIds);
}

View File

@ -38,12 +38,23 @@
SELECT
email
from user
WHERE name IN
WHERE name IN
<foreach collection="array" item="id" index="index"
open="(" close=")" separator=",">
#{id}
</foreach>
</select>
<select id="queryEmailByIds" parameterType="java.lang.String" resultType="java.lang.String">
SELECT
email
from user
WHERE id IN
<foreach collection="list" item="id" index="index"
open="(" close=")" separator=",">
#{id}
</foreach>
</select>
<!--修改密码-->

View File

@ -9,6 +9,7 @@ import io.metersphere.commons.utils.LogUtil;
import io.metersphere.dto.LoadTestDTO;
import io.metersphere.service.SystemParameterService;
import io.metersphere.service.UserService;
import io.metersphere.track.request.testreview.SaveTestCaseReviewRequest;
import org.springframework.mail.MailException;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
@ -18,7 +19,9 @@ import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Properties;
@ -135,5 +138,81 @@ public class MailService {
}
public void sendHtml(List<String> userIds, String type, SaveTestCaseReviewRequest reviewRequest) {
Long startTime = reviewRequest.getCreateTime();
Long endTime = reviewRequest.getEndTime();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String start = sdf.format(new Date(Long.parseLong(String.valueOf(startTime))));
String end = sdf.format(new Date(Long.parseLong(String.valueOf(endTime))));
JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();
List<SystemParameter> paramList = systemParameterService.getParamList(ParamConstants.Classify.MAIL.getValue());
javaMailSender.setDefaultEncoding("UTF-8");
javaMailSender.setProtocol("smtps");
for (SystemParameter p : paramList) {
if (p.getParamKey().equals("smtp.host")) {
javaMailSender.setHost(p.getParamValue());
}
if (p.getParamKey().equals("smtp.port")) {
javaMailSender.setPort(Integer.parseInt(p.getParamValue()));
}
if (p.getParamKey().equals("smtp.account")) {
javaMailSender.setUsername(p.getParamValue());
}
if (p.getParamKey().equals("smtp.password")) {
javaMailSender.setPassword(EncryptUtils.aesDecrypt(p.getParamValue()).toString());
}
}
Properties props = new Properties();
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.starttls.required", "true");
props.put("mail.smtp.timeout", "30000");
props.put("mail.smtp.connectiontimeout", "5000");
javaMailSender.setJavaMailProperties(props);
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
String html1 = "<!DOCTYPE html>\n" +
"<html lang=\"en\">\n" +
"<head>\n" +
" <meta charset=\"UTF-8\">\n" +
" <title>MeterSphere</title>\n" +
"</head>\n" +
"<body style=\"text-align: left\">\n" +
" <div>\n" +
" <p>" + reviewRequest.getCreator() + "发起的" + reviewRequest.getName() + "的计划开始时间是" + start + ",计划结束时间为" + end + "请跟进" + "</p>\n" +
" </div>\n" +
"</body>\n" +
"</html>";
String html2 = "";
String html3 = "";
try {
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
helper.setFrom(javaMailSender.getUsername());
helper.setSubject("测试评审任务通知");
String users[] = {};
List<String> emails = new ArrayList<>();
try {
emails = userService.queryEmailByIds(userIds);
} catch (Exception e) {
LogUtil.error("Recipient information is empty");
}
users = emails.toArray(new String[emails.size()]);
if (type.equals("reviewer")) {
helper.setText(html1, true);
} else if (type.equals("reviewer")) {
helper.setText(html2, true);
} else {
helper.setText(html3, true);
}
helper.setTo(users);
} catch (MessagingException e) {
e.printStackTrace();
}
try {
javaMailSender.send(mimeMessage);
} catch (MailException e) {
e.printStackTrace();
}
}
}

View File

@ -237,7 +237,7 @@ public class PerformanceTestService {
}
startEngine(loadTest, engine, request.getTriggerMode());
if (request.getTriggerMode().equals("SCHEDULE")) {
/* if (request.getTriggerMode().equals("SCHEDULE")) {
List<Notice> notice = null;
try {
notice = noticeService.queryNotice(request.getId());
@ -249,7 +249,7 @@ public class PerformanceTestService {
} catch (Exception e) {
e.printStackTrace();
}
}
}*/
return engine.getReportId();
}

View File

@ -61,9 +61,14 @@ public class UserService {
@Resource
private WorkspaceService workspaceService;
public List<String> queryEmail(String[] names){
return extUserMapper.queryEmails(names);
public List<String> queryEmail(String[] names) {
return extUserMapper.queryEmails(names);
}
public List<String> queryEmailByIds(List<String> userIds) {
return extUserMapper.queryEmailByIds(userIds);
}
public UserDTO insert(UserRequest user) {
checkUserParam(user);
//

View File

@ -46,4 +46,10 @@ public class TestCaseCommentService {
});
return testCaseComments;
}
public void deleteComment(String caseId) {
TestCaseCommentExample testCaseCommentExample = new TestCaseCommentExample();
testCaseCommentExample.createCriteria().andCaseIdEqualTo(caseId);
testCaseCommentMapper.deleteByExample(testCaseCommentExample);
}
}

View File

@ -14,6 +14,7 @@ import io.metersphere.commons.utils.MathUtils;
import io.metersphere.commons.utils.ServiceUtils;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.controller.request.member.QueryMemberRequest;
import io.metersphere.notice.service.MailService;
import io.metersphere.service.UserService;
import io.metersphere.track.dto.*;
import io.metersphere.track.request.testreview.QueryTestReviewRequest;
@ -60,6 +61,8 @@ public class TestCaseReviewService {
TestCaseMapper testCaseMapper;
@Resource
TestCaseReviewTestCaseMapper testCaseReviewTestCaseMapper;
@Resource
MailService mailService;
public void saveTestCaseReview(SaveTestCaseReviewRequest reviewRequest) {
checkCaseReviewExist(reviewRequest);
@ -88,6 +91,8 @@ public class TestCaseReviewService {
reviewRequest.setCreator(SessionUtils.getUser().getId());
reviewRequest.setStatus(TestCaseReviewStatus.Prepare.name());
testCaseReviewMapper.insert(reviewRequest);
mailService.sendHtml(userIds, "reviewer", reviewRequest);
}
public List<TestCaseReviewDTO> listCaseReview(QueryCaseReviewRequest request) {
@ -143,6 +148,7 @@ public class TestCaseReviewService {
testCaseReview.setUpdateTime(System.currentTimeMillis());
checkCaseReviewExist(testCaseReview);
testCaseReviewMapper.updateByPrimaryKeySelective(testCaseReview);
mailService.sendHtml(testCaseReview.getUserIds(), "reviewer", testCaseReview);
}
private void editCaseReviewer(SaveTestCaseReviewRequest testCaseReview) {

View File

@ -77,6 +77,8 @@ public class TestCaseService {
TestCaseIssueService testCaseIssueService;
@Resource
TestCaseReviewTestCaseMapper testCaseReviewTestCaseMapper;
@Resource
TestCaseCommentService testCaseCommentService;
public void addTestCase(TestCaseWithBLOBs testCase) {
testCase.setName(testCase.getName());
@ -155,6 +157,7 @@ public class TestCaseService {
example.createCriteria().andCaseIdEqualTo(testCaseId);
testPlanTestCaseMapper.deleteByExample(example);
testCaseIssueService.delTestCaseIssues(testCaseId);
testCaseCommentService.deleteComment(testCaseId);
return testCaseMapper.deleteByPrimaryKey(testCaseId);
}

View File

@ -22,13 +22,13 @@
@click="rerun(testId)">
{{ $t('report.test_execute_again') }}
</el-button>
<!-- <el-button :disabled="isReadOnly" type="info" plain size="mini" @click="exports(reportName)">
<el-button :disabled="isReadOnly" type="info" plain size="mini" @click="exportReport(reportName)">
{{$t('report.export')}}
</el-button>-->
<!--
<el-button :disabled="isReadOnly" type="warning" plain size="mini">
{{$t('report.compare')}}
</el-button>-->
</el-button>
<!--<el-button :disabled="isReadOnly" type="warning" plain size="mini">-->
<!--{{$t('report.compare')}}-->
<!--</el-button>-->
</el-row>
</el-col>
<el-col :span="8">
@ -54,10 +54,10 @@
<ms-report-test-overview :report="report" ref="testOverview"/>
</el-tab-pane>
<el-tab-pane :label="$t('report.test_request_statistics')">
<ms-report-request-statistics :report="report"/>
<ms-report-request-statistics :report="report" ref="requestStatistics"/>
</el-tab-pane>
<el-tab-pane :label="$t('report.test_error_log')">
<ms-report-error-log :report="report"/>
<ms-report-error-log :report="report" ref="errorLog"/>
</el-tab-pane>
<el-tab-pane :label="$t('report.test_log_details')">
<ms-report-log-details :report="report"/>
@ -65,6 +65,33 @@
</el-tabs>
</div>
<div class="report-export" v-show="reportExportVisible">
<!--<div class="report-export">-->
<el-card id="testOverview">
<template v-slot:header >
<slot name="header">
<span class="title">{{$t('report.test_overview')}}</span>
</slot>
</template>
<ms-report-test-overview :report="report" ref="testOverview"/>
</el-card>
<el-card id="requestStatistics" title="'requestStatistics'">
<template v-slot:header >
<slot name="header">
<span class="title">{{$t('report.test_request_statistics')}}</span>
</slot>
</template>
<ms-report-request-statistics :report="report" ref="requestStatistics"/>
</el-card>
<el-card id="errorLog" title="'errorLog'">
<template v-slot:header >
<slot name="header">
<span class="title">{{$t('report.test_error_log')}}</span>
</slot>
</template>
<ms-report-error-log :report="report" ref="errorLog"/>
</el-card>
</div>
</el-card>
<el-dialog :title="$t('report.test_stop_now_confirm')" :visible.sync="dialogFormVisible" width="30%">
@ -91,6 +118,9 @@ import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer";
import {checkoutTestManagerOrTestUser} from "@/common/js/utils";
import {exportPdf} from "../../../../common/js/utils";
import html2canvas from 'html2canvas';
export default {
name: "PerformanceReportView",
@ -123,6 +153,8 @@ export default {
isReadOnly: false,
websocket: null,
dialogFormVisible: false,
reportExportVisible: false,
isShow: true,
testPlan: {testResourcePoolId: null}
}
},
@ -247,7 +279,43 @@ export default {
this.$set(this.report, "status", 'Completed');
this.initReportTimeInfo();
window.console.log("socket closed.");
}
},
exportReport(name) {
this.result = {loading: true};
let result = this.result;
result.loading = true;
this.reportExportVisible = true;
let promises = [];
let canvasList = new Array(3);
let reset = this.exportReportReset;
this.$nextTick(function () {
setTimeout(() => {
promises.push(this.getCanvasPromise('testOverview', 0, canvasList));
promises.push(this.getCanvasPromise('requestStatistics', 1, canvasList));
promises.push(this.getCanvasPromise('errorLog', 2, canvasList));
Promise.all(promises).then(function (info) {
exportPdf(name, canvasList);
result.loading = false;
reset();
});
}, 1000);
})
},
exportReportReset() {
this.reportExportVisible = false;
this.isShow = true;
},
getCanvasPromise(id, index, canvasList) {
return new Promise(function(resolve, reject) {
html2canvas(document.getElementById(id), {
scale: 2
}).then(function(canvas) {
canvasList[index] = canvas;
resolve('success');
});
});
},
},
created() {
this.isReadOnly = false;
@ -305,14 +373,18 @@ export default {
<style scoped>
.ms-report-view-btns {
margin-top: 15px;
}
.ms-report-view-btns {
margin-top: 15px;
}
.ms-report-time-desc {
text-align: left;
display: block;
color: #5C7878;
}
.ms-report-time-desc {
text-align: left;
display: block;
color: #5C7878;
}
.report-export .el-card {
margin-bottom: 15px;
}
</style>

View File

@ -482,7 +482,7 @@
this.$warning(this.$t("test_track.case.relate_test_not_find"));
}
});
} else if (this.testCase.testId === 'other') {
} else if (this.testCase.testId === 'other' && this.testCase.method == 'auto') {
this.$warning(this.$t("test_track.case.other_relate_test_not_find"));
}
},

View File

@ -67,7 +67,7 @@
scale: 2
}).then(function(canvas) {
//
canvasList.splice(index, 0, canvas);
canvasList[index] = canvas;
resolve('success');
});
});

View File

@ -212,7 +212,7 @@
}
Promise.all(promises).then(function (info) {
exportPdf(canvasList);
exportPdf(name, canvasList);
result.loading = false;
});
},

View File

@ -1,6 +1,6 @@
<template>
<div v-loading="result.loading">
<div style="height:60vh;overflow-y: scroll">
<div class="comment-list">
<review-comment-item v-for="(comment,index) in comments" :key="index" :comment="comment"/>
<div v-if="comments.length === 0" style="text-align: center">
<i class="el-icon-chat-line-square" style="font-size: 15px;color: #8a8b8d;">
@ -64,8 +64,13 @@ export default {
</script>
<style scoped>
.send-btn {
margin-top: 5px;
width: 100%;
}
.send-btn {
margin-top: 5px;
width: 100%;
}
.comment-list {
overflow-y: scroll;
height: calc(70vh);
}
</style>

View File

@ -23,23 +23,27 @@
<el-col :span="12">
<el-button plain size="mini"
icon="el-icon-back"
@click="cancel">{{$t('test_track.return')}}
@click="cancel">{{ $t('test_track.return') }}
</el-button>
</el-col>
<el-col :span="12" class="head-right">
<span class="head-right-tip" v-if="index + 1 == testCases.length">
{{$t('test_track.plan_view.pre_case')}} : {{testCases[index - 1] ? testCases[index - 1].name : ''}}
{{ $t('test_track.plan_view.pre_case') }} : {{
testCases[index - 1] ? testCases[index - 1].name : ''
}}
</span>
<span class="head-right-tip" v-if="index + 1 != testCases.length">
{{$t('test_track.plan_view.next_case')}} : {{testCases[index + 1] ? testCases[index + 1].name : ''}}
{{ $t('test_track.plan_view.next_case') }} : {{
testCases[index + 1] ? testCases[index + 1].name : ''
}}
</span>
<el-button plain size="mini" icon="el-icon-arrow-up"
:disabled="index + 1 <= 1"
@click="handlePre()"/>
<span> {{index + 1}}/{{testCases.length}} </span>
<span> {{ index + 1 }}/{{ testCases.length }} </span>
<el-button plain size="mini" icon="el-icon-arrow-down"
:disabled="index + 1 >= testCases.length"
@click="handleNext()"/>
@ -57,7 +61,7 @@
<el-row style="margin-top: 0px;">
<el-col>
<el-divider content-position="left">{{testCase.name}}</el-divider>
<el-divider content-position="left">{{ testCase.name }}</el-divider>
</el-col>
</el-row>
@ -66,37 +70,38 @@
<div class="case_container">
<el-row>
<el-col :span="4" :offset="1">
<span class="cast_label">{{$t('test_track.case.priority')}}</span>
<span class="cast_item">{{testCase.priority}}</span>
<span class="cast_label">{{ $t('test_track.case.priority') }}</span>
<span class="cast_item">{{ testCase.priority }}</span>
</el-col>
<el-col :span="5">
<span class="cast_label">{{$t('test_track.case.case_type')}}</span>
<span class="cast_item" v-if="testCase.type == 'functional'">{{$t('commons.functional')}}</span>
<span class="cast_item" v-if="testCase.type == 'performance'">{{$t('commons.performance')}}</span>
<span class="cast_item" v-if="testCase.type == 'api'">{{$t('commons.api')}}</span>
<span class="cast_label">{{ $t('test_track.case.case_type') }}</span>
<span class="cast_item" v-if="testCase.type == 'functional'">{{ $t('commons.functional') }}</span>
<span class="cast_item"
v-if="testCase.type == 'performance'">{{ $t('commons.performance') }}</span>
<span class="cast_item" v-if="testCase.type == 'api'">{{ $t('commons.api') }}</span>
</el-col>
</el-row>
<el-row>
<el-col :span="4" :offset="1">
<span class="cast_label">{{$t('test_track.case.method')}}</span>
<span v-if="testCase.method == 'manual'">{{$t('test_track.case.manual')}}</span>
<span v-if="testCase.method == 'auto'">{{$t('test_track.case.auto')}}</span>
<span class="cast_label">{{ $t('test_track.case.method') }}</span>
<span v-if="testCase.method == 'manual'">{{ $t('test_track.case.manual') }}</span>
<span v-if="testCase.method == 'auto'">{{ $t('test_track.case.auto') }}</span>
</el-col>
<el-col :span="5">
<span class="cast_label">{{$t('test_track.case.module')}}</span>
<span class="cast_item">{{testCase.nodePath}}</span>
<span class="cast_label">{{ $t('test_track.case.module') }}</span>
<span class="cast_item">{{ testCase.nodePath }}</span>
</el-col>
<el-col :span="4" :offset="1">
<span class="cast_label">{{$t('test_track.plan.plan_project')}}</span>
<span class="cast_item">{{testCase.projectName}}</span>
<span class="cast_label">{{ $t('test_track.plan.plan_project') }}</span>
<span class="cast_item">{{ testCase.projectName }}</span>
</el-col>
</el-row>
<el-row>
<el-col :offset="1">
<span class="cast_label">{{$t('test_track.case.prerequisite')}}</span>
<span class="cast_item">{{testCase.prerequisite}}</span>
<span class="cast_label">{{ $t('test_track.case.prerequisite') }}</span>
<span class="cast_item">{{ testCase.prerequisite }}</span>
</el-col>
</el-row>
@ -123,7 +128,7 @@
<el-row v-if="testCase.method && testCase.method != 'auto'">
<el-col :span="20" :offset="1">
<div>
<span class="cast_label">{{$t('test_track.case.steps')}}</span>
<span class="cast_label">{{ $t('test_track.case.steps') }}</span>
</div>
<el-table
:data="testCase.steptResults"
@ -196,8 +201,9 @@
<el-row>
<el-col :span="15" :offset="1">
<div>
<span class="cast_label">{{$t('commons.remark')}}</span>
<span v-if="testCase.remark == null || testCase.remark == ''" style="color: darkgrey">{{$t('commons.not_filled')}}</span>
<span class="cast_label">{{ $t('commons.remark') }}</span>
<span v-if="testCase.remark == null || testCase.remark == ''"
style="color: darkgrey">{{ $t('commons.not_filled') }}</span>
</div>
<div>
<el-input :rows="3"
@ -240,7 +246,7 @@ import PerformanceTestDetail from "../../../plan/view/comonents/test/Performance
import ApiTestResult from "../../../plan/view/comonents/test/ApiTestResult";
import ApiTestDetail from "../../../plan/view/comonents/test/ApiTestDetail";
import TestPlanTestCaseStatusButton from "../../../plan/common/TestPlanTestCaseStatusButton";
import {listenGoBack, removeGoBackListener} from "../../../../../../common/js/utils";
import {listenGoBack, removeGoBackListener} from "@/common/js/utils";
import ReviewComment from "../../commom/ReviewComment";
export default {
@ -419,69 +425,69 @@ export default {
<style scoped>
.border-hidden >>> .el-textarea__inner {
border-style: hidden;
background-color: white;
color: #606266;
}
.border-hidden >>> .el-textarea__inner {
border-style: hidden;
background-color: white;
color: #606266;
}
.cast_label {
color: dimgray;
}
.cast_label {
color: dimgray;
}
.status-button {
padding-left: 4%;
}
.status-button {
padding-left: 4%;
}
.head-right {
text-align: right;
}
.head-right {
text-align: right;
}
.el-col:not(.test-detail) {
line-height: 50px;
}
.el-col:not(.test-detail) {
line-height: 50px;
}
.issues-edit >>> p {
line-height: 16px;
}
.issues-edit >>> p {
line-height: 16px;
}
.status-button {
float: right;
}
.status-button {
float: right;
}
.head-right-tip {
color: darkgrey;
}
.head-right-tip {
color: darkgrey;
}
.el-scrollbar {
height: 100%;
}
.el-scrollbar {
height: 100%;
}
.container {
height: 100vh;
}
.container {
height: 100vh;
}
.case_container > .el-row {
margin-top: 1%;
}
.case_container > .el-row {
margin-top: 1%;
}
.el-switch >>> .el-switch__label {
color: dimgray;
}
.el-switch >>> .el-switch__label {
color: dimgray;
}
.el-switch >>> .el-switch__label.is-active {
color: #409EFF;
}
.el-switch >>> .el-switch__label.is-active {
color: #409EFF;
}
.container >>> .el-card__body {
height: 90vh !important;
}
.container >>> .el-card__body {
height: calc(100vh - 70px);
}
.comment-card >>> .el-card__header {
padding: 0 20px;
}
.comment-card >>> .el-card__header {
padding: 0 20px;
}
.comment-card >>> .el-card__body {
height: 80vh !important;
}
.comment-card >>> .el-card__body {
height: calc(100vh - 120px);
}
</style>

View File

@ -5,7 +5,8 @@
<ms-table-header :is-tester-permission="true" :condition.sync="condition" @search="initTableData"
:show-create="false" :tip="$t('commons.search_by_name_or_id')">
<template v-slot:title>
<node-breadcrumb class="table-title" :nodes="selectParentNodes" @refresh="refresh" :title="$t('test_track.review_view.all_review')"/>
<node-breadcrumb class="table-title" :nodes="selectParentNodes" @refresh="refresh"
:title="$t('test_track.review_view.all_review')"/>
</template>
<template v-slot:button>
<ms-table-button :is-tester-permission="true" icon="el-icon-video-play"
@ -37,7 +38,7 @@
type="selection"/>
<el-table-column width="40" :resizable="false" align="center">
<template v-slot:default="scope">
<!-- <show-more-btn :is-show="scope.row.showMore" :buttons="buttons" :size="selectRows.size"/>-->
<!-- <show-more-btn :is-show="scope.row.showMore" :buttons="buttons" :size="selectRows.size"/>-->
</template>
</el-table-column>
<el-table-column
@ -97,7 +98,7 @@
<el-table-column
prop="reviewerName"
:label="$t('test_track.review.review_creator')"
:label="$t('test_track.review.reviewer')"
show-overflow-tooltip
>
</el-table-column>
@ -428,14 +429,14 @@ export default {
<style scoped>
.search {
margin-left: 10px;
width: 240px;
}
.search {
margin-left: 10px;
width: 240px;
}
.test-case-status, .el-table {
cursor: pointer;
}
.test-case-status, .el-table {
cursor: pointer;
}
</style>

View File

@ -204,7 +204,7 @@ export function getUUID() {
}
export function exportPdf(canvasList) {
export function exportPdf(name, canvasList) {
let pdf = new jsPDF('', 'pt', 'a4');
@ -212,6 +212,7 @@ export function exportPdf(canvasList) {
let currentHeight = 0;
for (let canvas of canvasList) {
if (canvas) {
let contentWidth = canvas.width;
let contentHeight = canvas.height;
@ -254,7 +255,7 @@ export function exportPdf(canvasList) {
}
}
pdf.save('stone.pdf');
pdf.save(name);
}