perf: 优化定时报告发送视图附件

This commit is contained in:
fit2cloud-chenyw 2022-07-11 16:39:36 +08:00
parent d6ef0859b4
commit a7da54227b
14 changed files with 143 additions and 53 deletions

View File

@ -1,10 +1,14 @@
package io.dataease.commons.utils; package io.dataease.commons.utils;
import java.io.File; import java.io.File;
import io.dataease.commons.model.excel.ExcelSheetModel; import io.dataease.commons.model.excel.ExcelSheetModel;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFCell; import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFRow; import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFSheet;
@ -14,18 +18,25 @@ import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.Font; import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.IndexedColors; import org.apache.poi.ss.usermodel.IndexedColors;
import cn.hutool.core.util.IdUtil;
public class ExcelUtils { public class ExcelUtils {
private static final String suffix = ".xls";
public static File exportExcel(List<ExcelSheetModel> sheets) throws Exception { public static File exportExcel(List<ExcelSheetModel> sheets, String fileName) throws Exception {
String fastUUID = IdUtil.fastUUID(); AtomicReference<String> realFileName = new AtomicReference<>(fileName);
File result = new File("/opt/dataease/data/" + fastUUID + ".xls");
HSSFWorkbook wb = new HSSFWorkbook(); HSSFWorkbook wb = new HSSFWorkbook();
sheets.forEach(sheet -> { sheets.forEach(sheet -> {
List<List<String>> details = sheet.getDatas(); List<List<String>> details = sheet.getDatas();
details.add(0, sheet.getHeads()); details.add(0, sheet.getHeads());
HSSFSheet curSheet = wb.createSheet(sheet.getSheetName()); String sheetName = sheet.getSheetName();
HSSFSheet curSheet = wb.createSheet(sheetName);
if (StringUtils.isBlank(fileName)) {
String cName = sheetName + suffix;
realFileName.set(cName);
}
CellStyle cellStyle = wb.createCellStyle(); CellStyle cellStyle = wb.createCellStyle();
Font font = wb.createFont(); Font font = wb.createFont();
font.setFontHeightInPoints((short) 12); font.setFontHeightInPoints((short) 12);
@ -52,6 +63,10 @@ public class ExcelUtils {
} }
} }
}); });
if (!StringUtils.endsWith(fileName, suffix)) {
realFileName.set(realFileName.get() + suffix);
}
File result = new File("/opt/dataease/data/" + realFileName.get());
wb.write(result); wb.write(result);
return result; return result;
} }

View File

@ -8,6 +8,7 @@ import io.dataease.commons.constants.ResourceAuthLevel;
import io.dataease.controller.request.chart.*; import io.dataease.controller.request.chart.*;
import io.dataease.controller.response.ChartDetail; import io.dataease.controller.response.ChartDetail;
import io.dataease.dto.chart.ChartViewDTO; import io.dataease.dto.chart.ChartViewDTO;
import io.dataease.dto.chart.ViewOption;
import io.dataease.plugins.common.base.domain.ChartViewWithBLOBs; import io.dataease.plugins.common.base.domain.ChartViewWithBLOBs;
import io.dataease.service.chart.ChartViewCacheService; import io.dataease.service.chart.ChartViewCacheService;
import io.dataease.service.chart.ChartViewService; import io.dataease.service.chart.ChartViewService;
@ -178,4 +179,10 @@ public class ChartViewController {
chartViewService.viewPropsSave(chartViewWithBLOBs); chartViewService.viewPropsSave(chartViewWithBLOBs);
} }
@ApiOperation("查询仪表板下视图选项")
@PostMapping("/viewOptions/{panelId}")
public List<ViewOption> viewOptions(@PathVariable("panelId") String panelId) {
return chartViewService.viewOptions(panelId);
}
} }

View File

@ -0,0 +1,13 @@
package io.dataease.dto.chart;
import lombok.Data;
import java.io.Serializable;
@Data
public class ViewOption implements Serializable {
private String id;
private String name;
}

View File

@ -2,6 +2,7 @@ package io.dataease.ext;
import io.dataease.controller.request.chart.ChartViewRequest; import io.dataease.controller.request.chart.ChartViewRequest;
import io.dataease.dto.chart.ChartViewDTO; import io.dataease.dto.chart.ChartViewDTO;
import io.dataease.dto.chart.ViewOption;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Select;
@ -50,4 +51,6 @@ public interface ExtChartViewMapper {
void deleteNoUseView(@Param("viewIds") List<String> viewIds,@Param("panelId") String panelId ); void deleteNoUseView(@Param("viewIds") List<String> viewIds,@Param("panelId") String panelId );
void initPanelChartViewCache(@Param("panelId") String panelId); void initPanelChartViewCache(@Param("panelId") String panelId);
List<ViewOption> chartOptions(@Param("panelId") String panelId);
} }

View File

@ -588,4 +588,8 @@
</foreach> </foreach>
</if> </if>
</delete> </delete>
<select id="chartOptions" resultType="io.dataease.dto.chart.ViewOption">
select id, title as name from chart_view where scene_id = #{panelId}
</select>
</mapper> </mapper>

View File

@ -27,6 +27,7 @@ import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.Optional;
@Service("emailTaskHandler") @Service("emailTaskHandler")
public class EmailTaskHandler extends TaskHandler implements Job { public class EmailTaskHandler extends TaskHandler implements Job {
@ -116,6 +117,13 @@ public class EmailTaskHandler extends TaskHandler implements Job {
emailXpackService.saveInstance(taskInstance); emailXpackService.saveInstance(taskInstance);
} }
protected void removeInstance(GlobalTaskInstance taskInstance) {
EmailXpackService emailXpackService = SpringContextUtil.getBean(EmailXpackService.class);
Optional.ofNullable(taskInstance).ifPresent(instance ->
Optional.ofNullable(taskInstance.getInstanceId()).ifPresent(instanceId ->
emailXpackService.delInstance(instanceId)));
}
protected void error(GlobalTaskInstance taskInstance, Throwable t) { protected void error(GlobalTaskInstance taskInstance, Throwable t) {
taskInstance.setStatus(ERROR); taskInstance.setStatus(ERROR);
taskInstance.setInfo(t.getMessage()); taskInstance.setInfo(t.getMessage());
@ -125,11 +133,12 @@ public class EmailTaskHandler extends TaskHandler implements Job {
@Async("priorityExecutor") @Async("priorityExecutor")
public void sendReport(GlobalTaskInstance taskInstance, XpackEmailTemplateDTO emailTemplateDTO, public void sendReport(GlobalTaskInstance taskInstance, XpackEmailTemplateDTO emailTemplateDTO,
SysUserEntity user) { SysUserEntity user) {
EmailXpackService emailXpackService = SpringContextUtil.getBean(EmailXpackService.class); EmailXpackService emailXpackService = SpringContextUtil.getBean(EmailXpackService.class);
try { try {
XpackEmailTaskRequest taskForm = emailXpackService.taskForm(taskInstance.getTaskId()); XpackEmailTaskRequest taskForm = emailXpackService.taskForm(taskInstance.getTaskId());
if (ObjectUtils.isEmpty(taskForm) || CronUtils.taskExpire(taskForm.getEndTime())) { if (ObjectUtils.isEmpty(taskForm) || CronUtils.taskExpire(taskForm.getEndTime())) {
removeInstance(taskInstance);
return; return;
} }
String panelId = emailTemplateDTO.getPanelId(); String panelId = emailTemplateDTO.getPanelId();

View File

@ -11,6 +11,7 @@ import io.dataease.plugins.xpack.email.dto.response.XpackEmailTemplateDTO;
import io.dataease.plugins.xpack.email.service.EmailXpackService; import io.dataease.plugins.xpack.email.service.EmailXpackService;
import io.dataease.service.chart.ViewExportExcel; import io.dataease.service.chart.ViewExportExcel;
import io.dataease.service.system.EmailService; import io.dataease.service.system.EmailService;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.ObjectUtils;
import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -31,9 +32,11 @@ public class EmailTaskViewHandler extends EmailTaskHandler {
public void sendReport(GlobalTaskInstance taskInstance, XpackEmailTemplateDTO emailTemplateDTO, public void sendReport(GlobalTaskInstance taskInstance, XpackEmailTemplateDTO emailTemplateDTO,
SysUserEntity user) { SysUserEntity user) {
EmailXpackService emailXpackService = SpringContextUtil.getBean(EmailXpackService.class); EmailXpackService emailXpackService = SpringContextUtil.getBean(EmailXpackService.class);
List<File> files = null;
try { try {
XpackEmailTaskRequest taskForm = emailXpackService.taskForm(taskInstance.getTaskId()); XpackEmailTaskRequest taskForm = emailXpackService.taskForm(taskInstance.getTaskId());
if (ObjectUtils.isEmpty(taskForm) || CronUtils.taskExpire(taskForm.getEndTime())) { if (ObjectUtils.isEmpty(taskForm) || CronUtils.taskExpire(taskForm.getEndTime())) {
removeInstance(taskInstance);
return; return;
} }
String panelId = emailTemplateDTO.getPanelId(); String panelId = emailTemplateDTO.getPanelId();
@ -51,13 +54,21 @@ public class EmailTaskViewHandler extends EmailTaskHandler {
.collect(Collectors.toList()); .collect(Collectors.toList());
PermissionProxy proxy = new PermissionProxy(); PermissionProxy proxy = new PermissionProxy();
proxy.setUserId(user.getUserId()); proxy.setUserId(user.getUserId());
List<File> files = viewExportExcel.export(panelId, viewIdList, proxy); files = viewExportExcel.export(panelId, viewIdList, proxy);
emailService.sendWithFiles(emailTemplateDTO.getRecipients(), emailTemplateDTO.getTitle(), contentStr, emailService.sendWithFiles(emailTemplateDTO.getRecipients(), emailTemplateDTO.getTitle(), contentStr, files);
files);
success(taskInstance); success(taskInstance);
} catch (Exception e) { } catch (Exception e) {
error(taskInstance, e); error(taskInstance, e);
LogUtil.error(e.getMessage(), e); LogUtil.error(e.getMessage(), e);
} finally {
if (CollectionUtils.isNotEmpty(files)) {
for (int i = 0; i < files.size(); i++) {
File file = files.get(i);
if (file.exists()) {
file.delete();
}
}
}
} }
} }
} }

View File

@ -1548,4 +1548,8 @@ public class ChartViewService {
} }
return res; return res;
} }
public List<ViewOption> viewOptions(String panelId) {
return extChartViewMapper.chartOptions(panelId);
}
} }

View File

@ -46,10 +46,8 @@ public class ViewExportExcel {
List<Map<String, Object>> components = gson.fromJson(componentsJson, tokenType); List<Map<String, Object>> components = gson.fromJson(componentsJson, tokenType);
ChartExtRequest chartExtRequest = buildViewRequest(componentsFilter(components, "custom", null, null)); ChartExtRequest chartExtRequest = buildViewRequest(componentsFilter(components, "custom", null, null));
List<File> results = new ArrayList<>(); List<File> results = new ArrayList<>();
List<ExcelSheetModel> sheets = viewIds.stream().map(viewId -> viewFiles(viewId, chartExtRequest)) List<ExcelSheetModel> sheets = viewIds.stream().map(viewId -> viewFiles(viewId, chartExtRequest)).collect(Collectors.toList());
.collect(Collectors.toList()); File excelFile = ExcelUtils.exportExcel(sheets, panelDto.getName());
File excelFile = ExcelUtils.exportExcel(sheets);
results.add(excelFile); results.add(excelFile);
return results; return results;
} }
@ -57,8 +55,8 @@ public class ViewExportExcel {
private List<Map<String, Object>> componentsFilter(List<Map<String, Object>> components, String type, private List<Map<String, Object>> componentsFilter(List<Map<String, Object>> components, String type,
String componentType, String serviceName) { String componentType, String serviceName) {
return components.stream().filter(component -> { return components.stream().filter(component -> {
String ctype = component.get("type").toString(); String ctype = Optional.ofNullable(component.get("type")).orElse("").toString();
String cComponentType = component.get("component").toString(); String cComponentType = Optional.ofNullable(component.get("component")).orElse("").toString();
String cServiceName = Optional.ofNullable(component.get("serviceName")).orElse("").toString(); String cServiceName = Optional.ofNullable(component.get("serviceName")).orElse("").toString();
boolean typeMatch = true; boolean typeMatch = true;

View File

@ -84,16 +84,18 @@ public class EmailService {
public void sendWithFiles(String to, String title, String content, List<File> files) { public void sendWithFiles(String to, String title, String content, List<File> files) {
if (StringUtils.isBlank(to)) if (StringUtils.isBlank(to))
return; return;
if (CollectionUtils.isEmpty(files)) {
send(to, title, content);
return;
}
MailInfo mailInfo = proxy().mailInfo(); MailInfo mailInfo = proxy().mailInfo();
checkMailInfo(mailInfo); checkMailInfo(mailInfo);
JavaMailSenderImpl driver = driver(mailInfo); JavaMailSenderImpl driver = driver(mailInfo);
MimeMessage mimeMessage = driver.createMimeMessage(); MimeMessage mimeMessage = driver.createMimeMessage();
String uuid = UUID.randomUUID().toString();
MimeBodyPart text = new MimeBodyPart(); MimeBodyPart text = new MimeBodyPart();
try { try {
text.setContent(content + "<br/><img style='width: 60%;' src='cid:" + uuid + "' />",
"text/html; charset=gb2312");
MimeMultipart multipart = new MimeMultipart(); MimeMultipart multipart = new MimeMultipart();
text.setText(content, "gb2312");
multipart.addBodyPart(text); multipart.addBodyPart(text);
multipart.setSubType("related"); multipart.setSubType("related");
for (int i = 0; i < files.size(); i++) { for (int i = 0; i < files.size(); i++) {

View File

@ -1,6 +1,4 @@
import request from '@/utils/request' import request from '@/utils/request'
import store from '@/store'
import { queryPanelComponents } from '@/api/panel/panel'
export function post(url, data, loading = false) { export function post(url, data, loading = false) {
return request({ return request({
@ -134,3 +132,10 @@ export function viewPropsSave(panelId, data) {
data data
}) })
} }
export const viewOptions = panelId => {
return request({
url: '/chart/view/viewOptions/' + panelId,
method: 'post'
})
}

View File

@ -4,7 +4,6 @@
ref="select" ref="select"
v-model="innerValues" v-model="innerValues"
v-popover:popover v-popover:popover
:title="labels"
popper-class="view-select-option" popper-class="view-select-option"
style="width: 100%;" style="width: 100%;"
multiple multiple
@ -14,10 +13,10 @@
@focus="_popoverShowFun" @focus="_popoverShowFun"
> >
<el-option <el-option
v-for="item in selectedViews" v-for="item in selectOptions"
:key="item.viewId" :key="item.id"
:label="item.title" :label="item.name"
:value="item.viewId" :value="item.id"
/> />
</el-select> </el-select>
@ -43,6 +42,7 @@
import { on, off } from './dom' import { on, off } from './dom'
import Preview from '@/components/canvas/components/Editor/Preview' import Preview from '@/components/canvas/components/Editor/Preview'
import { findOne } from '@/api/panel/panel' import { findOne } from '@/api/panel/panel'
import { viewOptions } from '@/api/chart/chart'
import { panelDataPrepare } from '@/components/canvas/utils/utils' import { panelDataPrepare } from '@/components/canvas/utils/utils'
export default { export default {
name: 'DeViewSelect', name: 'DeViewSelect',
@ -60,7 +60,6 @@ export default {
}, },
data() { data() {
return { return {
labels: [],
visible: false, visible: false,
placement: 'bottom', placement: 'bottom',
transition: 'el-zoom-in-top', transition: 'el-zoom-in-top',
@ -69,7 +68,8 @@ export default {
innerValues: [], innerValues: [],
panelHeight: 450, panelHeight: 450,
showPosition: 'email-task', showPosition: 'email-task',
viewLoaded: false viewLoaded: false,
selectOptions: []
} }
}, },
computed: { computed: {
@ -92,25 +92,19 @@ export default {
}, },
panelId(val, old) { panelId(val, old) {
if (val !== old) { if (val !== old) {
this.innerValues = []
this.loadView() this.loadView()
} }
}, },
selectedViews: { selectedViews: {
handler(val) { handler(val) {
if (!val || !JSON.stringify(val)) { if (!val || !JSON.stringify(val)) {
this.labels = []
this.innerValues = [] this.innerValues = []
return return
} }
const views = JSON.parse(JSON.stringify(val)) const viewIds = JSON.parse(JSON.stringify(val))
const viewIds = []
const names = []
views.forEach(item => {
viewIds.push(item.viewId)
names.push(item.title)
})
this.innerValues = JSON.parse(JSON.stringify(viewIds)) this.innerValues = JSON.parse(JSON.stringify(viewIds))
this.labels = JSON.parse(JSON.stringify(names))
}, },
deep: true deep: true
} }
@ -122,6 +116,7 @@ export default {
}) })
}, },
beforeDestroy() { beforeDestroy() {
this._selectClearFun()
off(document, 'mouseup', this._popoverHideFun) off(document, 'mouseup', this._popoverHideFun)
}, },
created() { created() {
@ -129,6 +124,8 @@ export default {
}, },
methods: { methods: {
loadView() { loadView() {
this._selectClearFun()
this.innerValues = this.value
this.viewLoaded = false this.viewLoaded = false
this.panelId && findOne(this.panelId).then(response => { this.panelId && findOne(this.panelId).then(response => {
this.panelInfo = { this.panelInfo = {
@ -143,9 +140,17 @@ export default {
this.viewLoaded = true this.viewLoaded = true
this.componentData = rsp.componentData this.componentData = rsp.componentData
this.canvasStyleData = rsp.componentStyle this.canvasStyleData = rsp.componentStyle
this.loadOptions()
}) })
}) })
}, },
loadOptions() {
this.panelId && viewOptions(this.panelId).then(res => {
this.selectOptions = res.data
this.init()
})
},
_updateH() { _updateH() {
this.$nextTick(() => { this.$nextTick(() => {
this.width = this.$refs.select.$el.getBoundingClientRect().width this.width = this.$refs.select.$el.getBoundingClientRect().width
@ -193,10 +198,13 @@ export default {
}) })
}, },
_selectClearFun() { _selectClearFun() {
const views = JSON.parse(JSON.stringify(this.selectedViews)) this.$store.dispatch('task/delPanelViews', this.panelId)
views.forEach(item => { },
this.$store.dispatch('task/delView', { 'panelId': this.panelId, 'viewId': item.viewId }) init() {
}) if (this.value && this.value.length) {
const viewIds = JSON.parse(JSON.stringify(this.value))
this.$store.dispatch('task/initPanelView', { 'panelId': this.panelId, 'viewIds': viewIds })
}
} }
} }

View File

@ -47,10 +47,6 @@ export default {
panelId: { panelId: {
type: String, type: String,
default: null default: null
},
chartTitle: {
type: String,
default: null
} }
}, },
data() { data() {
@ -85,7 +81,7 @@ export default {
taskChecked() { taskChecked() {
const panelId = this.panelId const panelId = this.panelId
return !!this.panelViews && !!this.panelViews[panelId] && !!this.panelViews[panelId].some(view => view.viewId === this.viewId) return !!this.panelViews && !!this.panelViews[panelId] && !!this.panelViews[panelId].some(viewId => viewId === this.viewId)
} }
}, },
watch: { watch: {
@ -100,7 +96,6 @@ export default {
} }
if (this.showPosition === 'email-task') { if (this.showPosition === 'email-task') {
this.isTaskChecked = !!this.taskChecked this.isTaskChecked = !!this.taskChecked
// this.emailTaskCheck(this.isTaskChecked)
} }
}, },
beforeDestroy() { beforeDestroy() {
@ -134,7 +129,7 @@ export default {
}, },
emailTaskCheck(val) { emailTaskCheck(val) {
if (val) { if (val) {
this.$store.dispatch('task/addView', { 'panelId': this.panelId, 'viewId': this.viewId, 'title': this.chartTitle }) this.$store.dispatch('task/addView', { 'panelId': this.panelId, 'viewId': this.viewId })
} else { } else {
this.$store.dispatch('task/delView', { 'panelId': this.panelId, 'viewId': this.viewId }) this.$store.dispatch('task/delView', { 'panelId': this.panelId, 'viewId': this.viewId })
} }

View File

@ -5,16 +5,16 @@ const state = {
const mutations = { const mutations = {
ADD_VIEW: (state, { panelId, viewId, title }) => { ADD_VIEW: (state, { panelId, viewId }) => {
if (!state.panelViews[panelId]) { if (!state.panelViews[panelId]) {
Vue.set(state.panelViews, panelId, []) Vue.set(state.panelViews, panelId, [])
} }
const views = state.panelViews[panelId] const viewIds = state.panelViews[panelId]
if (views.some(item => item.viewId === viewId)) { if (viewIds.some(item => item === viewId)) {
return return
} }
views.push({ viewId, title }) viewIds.push(viewId)
state.panelViews[panelId] = views state.panelViews[panelId] = viewIds
}, },
DEL_VIEW: (state, { panelId, viewId }) => { DEL_VIEW: (state, { panelId, viewId }) => {
@ -23,20 +23,36 @@ const mutations = {
let len = views.length let len = views.length
while (len--) { while (len--) {
const item = views[len] const item = views[len]
if (viewId === item.viewId) { if (viewId === item) {
views.splice(len, 1) views.splice(len, 1)
} }
} }
state.panelViews[panelId] = views state.panelViews[panelId] = views
},
DEL_PANEL_VIEW: (state, panelId) => {
const views = state.panelViews[panelId]
if (!views || !views.length) return
Vue.set(state.panelViews, panelId, [])
},
INIT_PANEL_VIEWS: (state, { panelId, viewIds }) => {
state.panelViews[panelId] = viewIds || []
} }
} }
const actions = { const actions = {
initPanelView({ commit }, data) {
commit('INIT_PANEL_VIEWS', data)
},
addView({ commit }, data) { addView({ commit }, data) {
commit('ADD_VIEW', data) commit('ADD_VIEW', data)
}, },
delView({ commit }, data) { delView({ commit }, data) {
commit('DEL_VIEW', data) commit('DEL_VIEW', data)
},
delPanelViews({ commit }, data) {
commit('DEL_PANEL_VIEW', data)
} }
} }