EmailScheduleServiceImpl.java 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. /*
  2. * <<
  3. * Davinci
  4. * ==
  5. * Copyright (C) 2016 - 2019 EDP
  6. * ==
  7. * Licensed under the Apache License, Version 2.0 (the "License");
  8. * you may not use this file except in compliance with the License.
  9. * You may obtain a copy of the License at
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. * >>
  17. *
  18. */
  19. package edp.davinci.service.impl;
  20. import com.alibaba.druid.util.StringUtils;
  21. import com.alibaba.fastjson.JSONObject;
  22. import edp.core.common.quartz.ScheduleService;
  23. import edp.core.enums.MailContentTypeEnum;
  24. import edp.core.exception.ServerException;
  25. import edp.core.model.MailAttachment;
  26. import edp.core.model.MailContent;
  27. import edp.core.utils.CollectionUtils;
  28. import edp.core.utils.MailUtils;
  29. import edp.davinci.core.common.Constants;
  30. import edp.davinci.core.enums.ActionEnum;
  31. import edp.davinci.core.enums.CronJobMediaType;
  32. import edp.davinci.core.enums.FileTypeEnum;
  33. import edp.davinci.core.enums.LogNameEnum;
  34. import edp.davinci.dao.*;
  35. import edp.davinci.dto.cronJobDto.CronJobConfig;
  36. import edp.davinci.dto.cronJobDto.CronJobContent;
  37. import edp.davinci.dto.cronJobDto.ExcelContent;
  38. import edp.davinci.dto.cronJobDto.MsgMailExcel;
  39. import edp.davinci.dto.dashboardDto.DashboardWithPortal;
  40. import edp.davinci.dto.projectDto.ProjectDetail;
  41. import edp.davinci.dto.widgetDto.WidgetWithRelationDashboardId;
  42. import edp.davinci.dto.widgetDto.WidgetWithVizId;
  43. import edp.davinci.model.*;
  44. import edp.davinci.service.ProjectService;
  45. import edp.davinci.service.excel.ExecutorUtils;
  46. import edp.davinci.service.excel.MsgWrapper;
  47. import edp.davinci.service.excel.WidgetContext;
  48. import edp.davinci.service.excel.WorkBookContext;
  49. import edp.davinci.service.screenshot.ImageContent;
  50. import org.slf4j.Logger;
  51. import org.slf4j.LoggerFactory;
  52. import org.springframework.beans.BeanUtils;
  53. import org.springframework.beans.factory.annotation.Autowired;
  54. import org.springframework.beans.factory.annotation.Value;
  55. import org.springframework.stereotype.Service;
  56. import java.util.*;
  57. import java.util.concurrent.CopyOnWriteArrayList;
  58. import java.util.concurrent.Future;
  59. import java.util.concurrent.TimeUnit;
  60. import java.util.concurrent.atomic.AtomicInteger;
  61. import java.util.stream.Collectors;
  62. import static edp.core.consts.Consts.AT_SYMBOL;
  63. import static edp.core.consts.Consts.EMPTY;
  64. @Service("emailScheduleService")
  65. public class EmailScheduleServiceImpl extends BaseScheduleService implements ScheduleService {
  66. private static final Logger scheduleLogger = LoggerFactory.getLogger(LogNameEnum.BUSINESS_SCHEDULE.getName());
  67. @Autowired
  68. private CronJobMapper cronJobMapper;
  69. @Autowired
  70. private MailUtils mailUtils;
  71. @Autowired
  72. private UserMapper userMapper;
  73. @Autowired
  74. private WidgetMapper widgetMapper;
  75. @Autowired
  76. private DashboardMapper dashboardMapper;
  77. @Autowired
  78. private MemDashboardWidgetMapper memDashboardWidgetMapper;
  79. @Autowired
  80. private DisplayMapper displayMapper;
  81. @Autowired
  82. private ProjectService projectService;
  83. @Value("${source.result-limit:1000000}")
  84. private int resultLimit;
  85. @Override
  86. public void execute(long jobId) throws Exception {
  87. CronJob cronJob = cronJobMapper.getById(jobId);
  88. if (null == cronJob || StringUtils.isEmpty(cronJob.getConfig())) {
  89. scheduleLogger.error("CronJob({}) config is empty", jobId);
  90. return;
  91. }
  92. cronJobMapper.updateExecLog(jobId, "");
  93. CronJobConfig cronJobConfig = null;
  94. try {
  95. cronJobConfig = JSONObject.parseObject(cronJob.getConfig(), CronJobConfig.class);
  96. } catch (Exception e) {
  97. scheduleLogger.error("Cronjob({}) parse config({}) error:{}", jobId, cronJob.getConfig(), e.getMessage());
  98. return;
  99. }
  100. if (StringUtils.isEmpty(cronJobConfig.getType())) {
  101. scheduleLogger.error("Cronjob({}) config type is empty", jobId);
  102. return;
  103. }
  104. scheduleLogger.info("CronJob({}) is start! --------------", jobId);
  105. List<ExcelContent> excels = null;
  106. List<ImageContent> images = null;
  107. User creator = userMapper.getById(cronJob.getCreateBy());
  108. if (cronJobConfig.getType().equals(CronJobMediaType.IMAGE.getType())) {
  109. images = generateImages(jobId, cronJobConfig, creator.getId());
  110. }
  111. if (cronJobConfig.getType().equals(CronJobMediaType.EXCEL.getType())) {
  112. excels = generateExcels(jobId, cronJobConfig, creator);
  113. }
  114. if (cronJobConfig.getType().equals(CronJobMediaType.IMAGEANDEXCEL.getType())) {
  115. images = generateImages(jobId, cronJobConfig, creator.getId());
  116. excels = generateExcels(jobId, cronJobConfig, creator);
  117. }
  118. List<MailAttachment> attachmentList = new ArrayList<>();
  119. if (!CollectionUtils.isEmpty(excels)) {
  120. excels.forEach(excel -> attachmentList.add(new MailAttachment(excel.getName() + FileTypeEnum.XLSX.getFormat(), excel.getFile())));
  121. }
  122. if (!CollectionUtils.isEmpty(images)) {
  123. images.forEach(image -> {
  124. String contentId = CronJobMediaType.IMAGE.getType() +
  125. Constants.UNDERLINE +
  126. UUID.randomUUID().toString().replaceAll(Constants.MINUS, EMPTY);
  127. attachmentList.add(new MailAttachment(contentId, image.getImageFile(), image.getUrl(), true));
  128. });
  129. }
  130. if (CollectionUtils.isEmpty(attachmentList)) {
  131. scheduleLogger.warn("CronJob({}) email content is empty", jobId);
  132. return;
  133. }
  134. scheduleLogger.info("CronJob({}) is ready to send email", cronJob.getId());
  135. MailContent mailContent = null;
  136. try {
  137. mailContent = MailContent.MailContentBuilder.builder()
  138. .withSubject(cronJobConfig.getSubject())
  139. .withTo(cronJobConfig.getTo())
  140. .withCc(cronJobConfig.getCc())
  141. .withBcc(cronJobConfig.getBcc())
  142. .withMainContent(MailContentTypeEnum.HTML)
  143. .withHtmlContent(cronJobConfig.getContent())
  144. .withTemplate(Constants.SCHEDULE_MAIL_TEMPLATE)
  145. .withAttachments(attachmentList)
  146. .build();
  147. } catch (ServerException e) {
  148. scheduleLogger.error("CronJob({}) build email content error:{}", jobId, e.getMessage());
  149. }
  150. mailUtils.sendMail(mailContent, null);
  151. scheduleLogger.info("CronJob({}) is finish! --------------", jobId);
  152. }
  153. /**
  154. * 根据job配置生成excel
  155. *
  156. * @param jobId
  157. * @param cronJobConfig
  158. * @return
  159. * @throws Exception
  160. */
  161. private List<ExcelContent> generateExcels(Long jobId, CronJobConfig cronJobConfig, User user) throws Exception {
  162. scheduleLogger.info("CronJob({}) fetching excel contents", jobId);
  163. Map<String, WorkBookContext> workBookContextMap = new HashMap<>();
  164. Map<String, Integer> vizOrderMap = new HashMap<>();
  165. Map<Long, Map<Long, Integer>> displayPageMap = new HashMap<>();
  166. Map<String, Integer> excelEntityOrderMap = new HashMap<>();
  167. List<CronJobContent> jobContentList = getCronJobContents(cronJobConfig, vizOrderMap, displayPageMap);
  168. if (CollectionUtils.isEmpty(jobContentList)) {
  169. scheduleLogger.warn("CronJob({}) excel entity is empty", jobId);
  170. return null;
  171. }
  172. for (CronJobContent cronJobContent : jobContentList) {
  173. int order = 0;
  174. if (cronJobContent.getContentType().equalsIgnoreCase(DISPLAY)) {
  175. if (vizOrderMap.containsKey(DISPLAY + AT_SYMBOL + cronJobContent.getId())) {
  176. order = vizOrderMap.get(DISPLAY + AT_SYMBOL + cronJobContent.getId());
  177. }
  178. Display display = displayMapper.getById(cronJobContent.getId());
  179. List<WidgetWithVizId> widgetsWithSlideIdList = widgetMapper.queryByDisplayId(cronJobContent.getId());
  180. if (display != null && !CollectionUtils.isEmpty(widgetsWithSlideIdList)) {
  181. ProjectDetail projectDetail = projectService.getProjectDetail(display.getProjectId(), user, false);
  182. boolean isMaintainer = projectService.isMaintainer(projectDetail, user);
  183. Map<Long, Integer> slidePageMap = displayPageMap.get(cronJobContent.getId());
  184. Map<Long, List<WidgetWithVizId>> slideWidgetsMap = widgetsWithSlideIdList.stream().collect(Collectors.groupingBy(WidgetWithVizId::getVizId));
  185. int slidePageSize = slideWidgetsMap.size();
  186. List<Long> slideIds = new ArrayList<>();
  187. if (CollectionUtils.isEmpty(cronJobContent.getItems())) {
  188. //all of slides in display
  189. slideIds.addAll(slideWidgetsMap.keySet());
  190. } else {
  191. //checked slides in display
  192. slideIds = cronJobContent.getItems();
  193. }
  194. for (Long slideId : slideIds) {
  195. List<WidgetWithVizId> widgets = slideWidgetsMap.get(slideId);
  196. if (CollectionUtils.isEmpty(widgets)) {
  197. continue;
  198. }
  199. List<WidgetContext> widgetContexts = new ArrayList<>();
  200. widgets.forEach(widget -> {
  201. widgetContexts.add(new WidgetContext(widget, isMaintainer, null));
  202. });
  203. WorkBookContext workBookContext = WorkBookContext.WorkBookContextBuilder.newBuilder()
  204. .withWidgets(widgetContexts)
  205. .withUser(user)
  206. .withResultLimit(resultLimit)
  207. .withTaskKey("Schedule_" + jobId)
  208. .withCustomLogger(scheduleLogger)
  209. .build();
  210. int page = slidePageMap.get(slideId);
  211. String workBookName = slidePageSize == 1 ? display.getName() : display.getName() + "(" + page + ")";
  212. workBookContextMap.put(workBookName, workBookContext);
  213. excelEntityOrderMap.put(workBookName, order + page);
  214. }
  215. }
  216. } else {
  217. if (vizOrderMap.containsKey(DASHBOARD + AT_SYMBOL + cronJobContent.getId())) {
  218. order = vizOrderMap.get(DASHBOARD + AT_SYMBOL + cronJobContent.getId());
  219. }
  220. DashboardWithPortal dashboard = dashboardMapper.getDashboardWithPortalAndProject(cronJobContent.getId());
  221. excelEntityOrderMap.put(dashboard.getName(), vizOrderMap.get(DASHBOARD + AT_SYMBOL + cronJobContent.getId()));
  222. ProjectDetail projectDetail = projectService.getProjectDetail(dashboard.getProject().getId(), user, false);
  223. boolean isMaintainer = projectService.isMaintainer(projectDetail, user);
  224. List<WidgetWithRelationDashboardId> widgets = widgetMapper.getByDashboard(cronJobContent.getId());
  225. if (!CollectionUtils.isEmpty(widgets)) {
  226. List<MemDashboardWidget> mdws = memDashboardWidgetMapper.getByDashboardId(dashboard.getId());
  227. Map<Long, MemDashboardWidget> mdwMap = mdws.stream().collect(Collectors.toMap(o -> o.getWidgetId(), o -> o, (oldV, newV) -> oldV));
  228. List<WidgetContext> widgetContexts = new ArrayList<>();
  229. widgets.forEach(w -> {
  230. Widget widget = new Widget();
  231. BeanUtils.copyProperties(w, widget);
  232. WidgetContext context = new WidgetContext(widget, dashboard, mdwMap.get(widget.getId()), null);
  233. context.setIsMaintainer(isMaintainer);
  234. widgetContexts.add(context);
  235. });
  236. WorkBookContext workBookContext = WorkBookContext.WorkBookContextBuilder.newBuilder()
  237. .withWidgets(widgetContexts)
  238. .withUser(user)
  239. .withResultLimit(resultLimit)
  240. .withTaskKey("Schedule_" + jobId)
  241. .withCustomLogger(scheduleLogger)
  242. .build();
  243. workBookContextMap.put(dashboard.getName(), workBookContext);
  244. excelEntityOrderMap.put(dashboard.getName(), order);
  245. }
  246. }
  247. }
  248. if (CollectionUtils.isEmpty(workBookContextMap)) {
  249. scheduleLogger.warn("CronJob({}) workbook context is empty", jobId);
  250. return null;
  251. }
  252. List<ExcelContent> excelContents = new CopyOnWriteArrayList<>();
  253. Map<String, Future<String>> excelPathFutureMap = new LinkedHashMap<>();
  254. int contextSize = workBookContextMap.size();
  255. final AtomicInteger index = new AtomicInteger(1);
  256. workBookContextMap.forEach((name, context) -> {
  257. scheduleLogger.info("CronJob({}) submit workbook task:{}, thread:{}, total:{}", jobId, name, index, contextSize);
  258. try {
  259. String uuid = UUID.randomUUID().toString().replace("-", EMPTY);
  260. context.setWrapper(new MsgWrapper(new MsgMailExcel(jobId), ActionEnum.MAIL, uuid));
  261. excelPathFutureMap.put(name, ExecutorUtils.submitWorkbookTask(context, scheduleLogger));
  262. }catch (Exception e) {
  263. scheduleLogger.error("Cronjob({}) submit workbook task error, thread:{}", jobId, index.get());
  264. scheduleLogger.error(e.getMessage(), e);
  265. }finally {
  266. index.incrementAndGet();
  267. }
  268. });
  269. excelPathFutureMap.forEach((name, future) -> {
  270. String excelPath = null;
  271. try {
  272. excelPath = future.get(1, TimeUnit.HOURS);
  273. scheduleLogger.info("CronJob({}) workbook task:{} finish", jobId, name);
  274. } catch (Exception e) {
  275. scheduleLogger.info("CronJob({}) workbook task:{} error", jobId, name);
  276. scheduleLogger.error(e.getMessage(), e);
  277. }
  278. if (!StringUtils.isEmpty(excelPath)) {
  279. excelContents.add(new ExcelContent(excelEntityOrderMap.get(name), name, excelPath));
  280. }
  281. });
  282. excelContents.sort(Comparator.comparing(ExcelContent::getOrder));
  283. scheduleLogger.info("CronJob({}) fetched excel contents, count:{}", jobId, excelContents.size());
  284. return excelContents.isEmpty() ? null : excelContents;
  285. }
  286. }