From 32d5783bc2a9d2cdb1b712cd12b7a5e118ea1def Mon Sep 17 00:00:00 2001 From: "LAPTOP-SB56SG4Q\\86185" Date: Mon, 21 Mar 2022 19:08:48 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E5=BC=80=E6=BA=90=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E6=9D=90=E6=96=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 5 +- plugin.xml | 34 +++++ .../zk/stream/excel/HttpHandlerProvider.java | 20 +++ .../zk/stream/excel/UrlAliasBridge.java | 17 +++ .../excel/db/ExcelAccessProviderImpl.java | 31 ++++ .../stream/excel/db/dao/ExcelExportDao.java | 18 +++ .../excel/db/entity/ExcelExportEntity.java | 136 ++++++++++++++++++ .../excel/db/service/ExcelExportService.java | 60 ++++++++ .../plugin/zk/stream/excel/dto/ExportDto.java | 88 ++++++++++++ .../zk/stream/excel/export/ExportAction.java | 103 +++++++++++++ .../excel/provider/DownloadProvider.java | 80 +++++++++++ .../stream/excel/provider/ExportProvider.java | 73 ++++++++++ .../excel/provider/MessageProvider.java | 53 +++++++ .../zk/stream/excel/util/Constants.java | 9 ++ .../zk/stream/excel/util/ExportUtil.java | 115 +++++++++++++++ 使用说明文档.docx | Bin 0 -> 31936 bytes 16 files changed, 841 insertions(+), 1 deletion(-) create mode 100644 plugin.xml create mode 100644 src/main/java/com/fr/plugin/zk/stream/excel/HttpHandlerProvider.java create mode 100644 src/main/java/com/fr/plugin/zk/stream/excel/UrlAliasBridge.java create mode 100644 src/main/java/com/fr/plugin/zk/stream/excel/db/ExcelAccessProviderImpl.java create mode 100644 src/main/java/com/fr/plugin/zk/stream/excel/db/dao/ExcelExportDao.java create mode 100644 src/main/java/com/fr/plugin/zk/stream/excel/db/entity/ExcelExportEntity.java create mode 100644 src/main/java/com/fr/plugin/zk/stream/excel/db/service/ExcelExportService.java create mode 100644 src/main/java/com/fr/plugin/zk/stream/excel/dto/ExportDto.java create mode 100644 src/main/java/com/fr/plugin/zk/stream/excel/export/ExportAction.java create mode 100644 src/main/java/com/fr/plugin/zk/stream/excel/provider/DownloadProvider.java create mode 100644 src/main/java/com/fr/plugin/zk/stream/excel/provider/ExportProvider.java create mode 100644 src/main/java/com/fr/plugin/zk/stream/excel/provider/MessageProvider.java create mode 100644 src/main/java/com/fr/plugin/zk/stream/excel/util/Constants.java create mode 100644 src/main/java/com/fr/plugin/zk/stream/excel/util/ExportUtil.java create mode 100644 使用说明文档.docx diff --git a/README.md b/README.md index 21598c0..5505a09 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ # open-JSD-8101 -JSD-8101 异步流式导出(导出到服务器、异步下载) \ No newline at end of file +JSD-8101 异步流式导出(导出到服务器、异步下载)\ +免责说明:该源码为第三方爱好者提供,不保证源码和方案的可靠性,也不提供任何形式的源码教学指导和协助!\ +仅作为开发者学习参考使用!禁止用于任何商业用途!\ +为保护开发者隐私,开发者信息已隐去!若原开发者希望公开自己的信息,可联系hugh处理。 \ No newline at end of file diff --git a/plugin.xml b/plugin.xml new file mode 100644 index 0000000..a443f09 --- /dev/null +++ b/plugin.xml @@ -0,0 +1,34 @@ + + + com.fr.plugin.zk.jsd8101.excel + + yes + 1.0.1 + 10.0 + 2020-08-20 + fr.open + + + + ]]> + + com.fr.plugin.zk + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/java/com/fr/plugin/zk/stream/excel/HttpHandlerProvider.java b/src/main/java/com/fr/plugin/zk/stream/excel/HttpHandlerProvider.java new file mode 100644 index 0000000..29e939f --- /dev/null +++ b/src/main/java/com/fr/plugin/zk/stream/excel/HttpHandlerProvider.java @@ -0,0 +1,20 @@ +package com.fr.plugin.zk.stream.excel; + +import com.fr.decision.fun.HttpHandler; +import com.fr.decision.fun.impl.AbstractHttpHandlerProvider; +import com.fr.plugin.zk.stream.excel.provider.DownloadProvider; +import com.fr.plugin.zk.stream.excel.provider.ExportProvider; +import com.fr.plugin.zk.stream.excel.provider.MessageProvider; + +public class HttpHandlerProvider extends AbstractHttpHandlerProvider { + + @Override + public HttpHandler[] registerHandlers() { + return new HttpHandler[]{ + new ExportProvider(), + new MessageProvider(), + new DownloadProvider() + }; + } + +} diff --git a/src/main/java/com/fr/plugin/zk/stream/excel/UrlAliasBridge.java b/src/main/java/com/fr/plugin/zk/stream/excel/UrlAliasBridge.java new file mode 100644 index 0000000..024ed66 --- /dev/null +++ b/src/main/java/com/fr/plugin/zk/stream/excel/UrlAliasBridge.java @@ -0,0 +1,17 @@ +package com.fr.plugin.zk.stream.excel; + +import com.fr.decision.fun.impl.AbstractURLAliasProvider; +import com.fr.decision.webservice.url.alias.URLAlias; +import com.fr.decision.webservice.url.alias.URLAliasFactory; + +public class UrlAliasBridge extends AbstractURLAliasProvider { + @Override + public URLAlias[] registerAlias() { + return new URLAlias[]{ + URLAliasFactory.createPluginAlias("/stream/export", "/stream/export", false), + URLAliasFactory.createPluginAlias("/stream/message", "/stream/message", false), + URLAliasFactory.createPluginAlias("/stream/download", "/stream/download", false), + }; + } + +} diff --git a/src/main/java/com/fr/plugin/zk/stream/excel/db/ExcelAccessProviderImpl.java b/src/main/java/com/fr/plugin/zk/stream/excel/db/ExcelAccessProviderImpl.java new file mode 100644 index 0000000..5a8e3ee --- /dev/null +++ b/src/main/java/com/fr/plugin/zk/stream/excel/db/ExcelAccessProviderImpl.java @@ -0,0 +1,31 @@ +package com.fr.plugin.zk.stream.excel.db; + +import com.fr.db.fun.impl.AbstractDBAccessProvider; +import com.fr.plugin.zk.stream.excel.db.dao.ExcelExportDao; +import com.fr.plugin.zk.stream.excel.db.entity.ExcelExportEntity; +import com.fr.plugin.zk.stream.excel.db.service.ExcelExportService; +import com.fr.stable.db.accessor.DBAccessor; +import com.fr.stable.db.dao.BaseDAO; +import com.fr.stable.db.dao.DAOProvider; + +public class ExcelAccessProviderImpl extends AbstractDBAccessProvider { + @Override + public DAOProvider[] registerDAO() { + return new DAOProvider[]{new DAOProvider() { + @Override + public Class getEntityClass() { + return ExcelExportEntity.class; + } + + @Override + public Class getDAOClass() { + return ExcelExportDao.class; + } + }}; + } + + @Override + public void onDBAvailable(DBAccessor dbAccessor) { + ExcelExportService.getSingleton().init(dbAccessor); + } +} diff --git a/src/main/java/com/fr/plugin/zk/stream/excel/db/dao/ExcelExportDao.java b/src/main/java/com/fr/plugin/zk/stream/excel/db/dao/ExcelExportDao.java new file mode 100644 index 0000000..57961e8 --- /dev/null +++ b/src/main/java/com/fr/plugin/zk/stream/excel/db/dao/ExcelExportDao.java @@ -0,0 +1,18 @@ +package com.fr.plugin.zk.stream.excel.db.dao; + +import com.fr.plugin.zk.stream.excel.db.entity.ExcelExportEntity; +import com.fr.stable.db.dao.BaseDAO; +import com.fr.stable.db.session.DAOSession; + + +public class ExcelExportDao extends BaseDAO { + + public ExcelExportDao(DAOSession daoSession) { + super(daoSession); + } + + @Override + protected Class getEntityClass() { + return ExcelExportEntity.class; + } +} diff --git a/src/main/java/com/fr/plugin/zk/stream/excel/db/entity/ExcelExportEntity.java b/src/main/java/com/fr/plugin/zk/stream/excel/db/entity/ExcelExportEntity.java new file mode 100644 index 0000000..742ff53 --- /dev/null +++ b/src/main/java/com/fr/plugin/zk/stream/excel/db/entity/ExcelExportEntity.java @@ -0,0 +1,136 @@ +package com.fr.plugin.zk.stream.excel.db.entity; + +import com.fr.stable.db.entity.BaseEntity; +import com.fr.third.javax.persistence.Column; +import com.fr.third.javax.persistence.Entity; +import com.fr.third.javax.persistence.Lob; +import com.fr.third.javax.persistence.Table; + +import java.util.Date; +import java.util.UUID; + + +@Entity +@Table(name = "fine_export_stream_excel") +public class ExcelExportEntity extends BaseEntity { + + //模板路径 + @Column(name = "path") + private String path; + + //模板参数 + @Lob + @Column(name = "parameters") + private String parameters; + + //导出状态 导出中、完成、失败 + @Column(name = "status") + private String status = "导出中"; + + @Column(name = "start_time") + private Date startTime; + + @Column(name = "end_time") + private Date endTime; + + @Column(name = "filename") + private String filename; + + @Column(name = "file_path") + private String filePath; + + @Column(name = "file_size") + private Integer fileSize; + + @Lob + @Column(name = "message") + private String message; + + @Column(name = "username") + private String username; + + public ExcelExportEntity() { + this.setId(UUID.randomUUID().toString()); + } + + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public String getParameters() { + return parameters; + } + + public void setParameters(String parameters) { + this.parameters = parameters; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public Date getStartTime() { + return startTime; + } + + public void setStartTime(Date startTime) { + this.startTime = startTime; + } + + public Date getEndTime() { + return endTime; + } + + public void setEndTime(Date endTime) { + this.endTime = endTime; + } + + public Integer getFileSize() { + return fileSize; + } + + public void setFileSize(Integer fileSize) { + this.fileSize = fileSize; + } + + public String getFilePath() { + return filePath; + } + + public void setFilePath(String filePath) { + this.filePath = filePath; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getFilename() { + return filename; + } + + public void setFilename(String filename) { + this.filename = filename; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } +} diff --git a/src/main/java/com/fr/plugin/zk/stream/excel/db/service/ExcelExportService.java b/src/main/java/com/fr/plugin/zk/stream/excel/db/service/ExcelExportService.java new file mode 100644 index 0000000..77eadaf --- /dev/null +++ b/src/main/java/com/fr/plugin/zk/stream/excel/db/service/ExcelExportService.java @@ -0,0 +1,60 @@ +package com.fr.plugin.zk.stream.excel.db.service; + +import com.fr.log.FineLoggerFactory; +import com.fr.plugin.zk.stream.excel.db.dao.ExcelExportDao; +import com.fr.plugin.zk.stream.excel.db.entity.ExcelExportEntity; +import com.fr.stable.db.accessor.DBAccessor; +import com.fr.stable.db.action.DBAction; +import com.fr.stable.db.dao.DAOContext; +import com.fr.stable.query.QueryFactory; +import com.fr.stable.query.condition.QueryCondition; +import com.fr.stable.query.restriction.impl.EqRestriction; + +public class ExcelExportService { + + private ExcelExportService() { + } + + private static class Hoder { + private static ExcelExportService excelExportService = new ExcelExportService(); + } + + public static ExcelExportService getSingleton() { + return Hoder.excelExportService; + } + + private DBAccessor accessor = null; + + public void init(DBAccessor accessor) { + this.accessor = accessor; + } + + + public void addOrUpdate(ExcelExportEntity entity) { + try { + accessor.runDMLAction(new DBAction() { + @Override + public Boolean run(DAOContext context) throws Exception { + context.getDAO(ExcelExportDao.class).addOrUpdate(entity); + return true; + } + }); + } catch (Exception e) { + FineLoggerFactory.getLogger().error("存储导出记录失败:" + e.getMessage(), e); + } + } + + public ExcelExportEntity queryById(String id) throws Exception { + ExcelExportEntity entity = accessor.runQueryAction(new DBAction() { + @Override + public ExcelExportEntity run(DAOContext context) throws Exception { + QueryCondition queryCondition = QueryFactory.create(); + queryCondition.addRestriction(new EqRestriction("id", id)); + return context.getDAO(ExcelExportDao.class).findOne(queryCondition); + } + }); + return entity; + } + + +} diff --git a/src/main/java/com/fr/plugin/zk/stream/excel/dto/ExportDto.java b/src/main/java/com/fr/plugin/zk/stream/excel/dto/ExportDto.java new file mode 100644 index 0000000..18ab78d --- /dev/null +++ b/src/main/java/com/fr/plugin/zk/stream/excel/dto/ExportDto.java @@ -0,0 +1,88 @@ +package com.fr.plugin.zk.stream.excel.dto; + +import com.fr.base.ServerConfig; +import com.fr.log.FineLoggerFactory; +import com.fr.plugin.zk.stream.excel.util.ExportUtil; +import com.fr.stable.StringUtils; +import com.fr.third.org.apache.commons.collections4.CollectionUtils; +import com.fr.third.org.apache.commons.collections4.MapUtils; + +import javax.servlet.http.HttpServletRequest; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.HashMap; + +/** + * @author fr.open + * @Project name finereport-gradle + * @description: + * @created on 2021-06-24 16:36. + * @Modified By + */ +public class ExportDto { + + private String path; + + private String filename; + + private String username; + + private String fullUrl; + + private HashMap parameters = new HashMap<>(); + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public HashMap getParameters() { + return parameters; + } + + public void setParameters(HashMap parameters) { + this.parameters = parameters; + } + + public String getFilename() { + return filename; + } + + public void setFilename(String filename) { + this.filename = filename; + } + + public String getUsername() { + return username; + } + public void setUsername(String username) { + this.username = username; + } + + public void setFullPath(HttpServletRequest req){ + String path = getPath(); + HashMap parameters = getParameters(); + String root = ExportUtil.buildServerURL(req) + req.getContextPath() + "/" + ServerConfig.getInstance().getServletName(); + String url = null; + if(StringUtils.isNotBlank(path)){ + try { + url = root + "/view/report?viewlet=" + URLEncoder.encode(path, "UTF-8") + "&op=export&format=excel&extype=stream"; + } catch (UnsupportedEncodingException e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + }else{ + url = root + "/view/report?op=export&format=excel&extype=stream"; + } + if(MapUtils.isNotEmpty(parameters)){ + url = ExportUtil.appendUrlPara(url,getParameters()); + } + this.fullUrl = url; + } + + public String getFullUrl() { + return fullUrl; + } +} diff --git a/src/main/java/com/fr/plugin/zk/stream/excel/export/ExportAction.java b/src/main/java/com/fr/plugin/zk/stream/excel/export/ExportAction.java new file mode 100644 index 0000000..beeaa7b --- /dev/null +++ b/src/main/java/com/fr/plugin/zk/stream/excel/export/ExportAction.java @@ -0,0 +1,103 @@ +package com.fr.plugin.zk.stream.excel.export; + +import com.fr.general.http.HttpClient; +import com.fr.io.context.ResourceModuleContext; +import com.fr.io.repository.ResourceRepository; +import com.fr.io.repository.base.fs.FileSystemRepository; +import com.fr.log.FineLoggerFactory; +import com.fr.plugin.zk.stream.excel.db.entity.ExcelExportEntity; +import com.fr.plugin.zk.stream.excel.db.service.ExcelExportService; +import com.fr.plugin.zk.stream.excel.dto.ExportDto; +import com.fr.plugin.zk.stream.excel.util.ExportUtil; +import com.fr.stable.StringUtils; +import com.fr.third.org.apache.poi.util.IOUtils; +import com.fr.workspace.WorkContext; + +import javax.servlet.http.HttpServletRequest; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Date; +import java.util.HashMap; +import java.util.UUID; + +/** + * @author fr.open + * @Project finereport-gradle + * @description: + * @created on 2021-06-24 15:52. + * @Modified By + */ +public class ExportAction implements Runnable { + + private HttpServletRequest req; + private ExportDto export; + + public ExportAction(HttpServletRequest req, ExportDto exportDto) { + this.req = req; + this.export = exportDto; + } + + @Override + public void run() { + String path = this.export.getPath(); + HashMap parameters = this.export.getParameters(); + String url = this.export.getFullUrl(); + ExcelExportService excelExportService = ExcelExportService.getSingleton(); + ExcelExportEntity entity = new ExcelExportEntity(); + entity.setPath(path); + entity.setParameters(ExportUtil.writeToJson(parameters)); + entity.setStartTime(new Date()); + entity.setUsername(this.export.getUsername()); + excelExportService.addOrUpdate(entity); + HttpClient client = new HttpClient(url); + client.asGet(); + //file + String type = client.getHeaderField("extension"); + if ("xlsx".equals(type)) { + try { + String filename = client.getHeaderField("Content-disposition").replace("attachment; filename=", ""); + InputStream inputStream = client.getResponseStream(); + Thread.sleep(1000); + filename = new String(filename.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); + byte[] fileByte = new byte[inputStream.available()]; + IOUtils.readFully(inputStream, fileByte); + inputStream.close(); + String filepath = "/resources/excel/" + UUID.randomUUID().toString() + ".xlsx"; + if (StringUtils.isNotBlank(this.export.getFilename())) { + entity.setFilename(this.export.getFilename()); + } else { + entity.setFilename(filename.replace(".xlsx", "")); + } + entity.setStatus("完成"); + entity.setFilePath(filepath); + entity.setFileSize(fileByte.length); + ResourceRepository repository = ResourceModuleContext.getManager().getCurrent(); + if (repository instanceof FileSystemRepository) { + //本地 远程 + WorkContext.getWorkResource().write(filepath, fileByte); + } else { + //集群 文件节点 + ResourceModuleContext.getManager().getCurrent().write(filepath, fileByte); + } + entity.setEndTime(new Date()); + excelExportService.addOrUpdate(entity); + } catch (Exception e) { + String message = "导出失败。详情:" + e.getMessage(); + entity.setEndTime(new Date()); + entity.setStatus("失败"); + entity.setMessage(message); + FineLoggerFactory.getLogger().error(message, e); + } + } else { + // 存数据库 + String text = client.getResponseText(); + FineLoggerFactory.getLogger().error("导出失败。" + client.getResponseText()); + entity.setEndTime(new Date()); + entity.setStatus("失败"); + entity.setMessage(text); + excelExportService.addOrUpdate(entity); + } + } + + +} diff --git a/src/main/java/com/fr/plugin/zk/stream/excel/provider/DownloadProvider.java b/src/main/java/com/fr/plugin/zk/stream/excel/provider/DownloadProvider.java new file mode 100644 index 0000000..4c9c25b --- /dev/null +++ b/src/main/java/com/fr/plugin/zk/stream/excel/provider/DownloadProvider.java @@ -0,0 +1,80 @@ +package com.fr.plugin.zk.stream.excel.provider; + +import com.fr.decision.fun.impl.BaseHttpHandler; +import com.fr.io.context.ResourceModuleContext; +import com.fr.log.FineLoggerFactory; +import com.fr.plugin.zk.stream.excel.db.entity.ExcelExportEntity; +import com.fr.plugin.zk.stream.excel.db.service.ExcelExportService; +import com.fr.plugin.zk.stream.excel.util.ExportUtil; +import com.fr.stable.StringUtils; +import com.fr.third.springframework.web.bind.annotation.RequestMethod; +import com.fr.third.v2.org.apache.poi.util.IOUtils; +import com.fr.web.utils.WebUtils; +import com.fr.workspace.WorkContext; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +public class DownloadProvider extends BaseHttpHandler { + + @Override + public RequestMethod getMethod() { + return RequestMethod.GET; + } + + @Override + public String getPath() { + return "/stream/download"; + } + + @Override + public boolean isPublic() { + return false; + } + + + @Override + public void handle(HttpServletRequest req, HttpServletResponse res) throws Exception { + String username = ExportUtil.getLoginUserAndResponse(req, res); + if (StringUtils.isBlank(username)) { + return; + } + String id = WebUtils.getHTTPRequestParameter(req, "id"); + ExcelExportEntity entity = ExcelExportService.getSingleton().queryById(id); + if (entity == null) { + res.setContentType("text/plain"); + WebUtils.printAsString(res, "找不到对应数据"); + } else { + try { + InputStream inputStream = read(entity.getFilePath()); + if (inputStream == null) { + throw new Exception("找不到文件:" + entity.getFilePath()); + } + res.setHeader("Content-Disposition", "attachment; filename=" + + new String(entity.getFilename().concat(".xlsx").getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1)); + res.setContentLength(entity.getFileSize()); + res.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + IOUtils.copy(inputStream, res.getOutputStream()); + } catch (Exception e) { + res.setContentType("text/plain"); + WebUtils.printAsString(res, "找不到文件:" + entity.getFilePath()); + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + } + } + + private InputStream read(String path) { + // 防止某些情况找不到文件 + InputStream inputStream = ResourceModuleContext.getManager().getCurrent().read(path); + if (inputStream == null) { + inputStream = ResourceModuleContext.getManager().getCurrent().openStream(path); + } + if (inputStream == null) { + inputStream = WorkContext.getWorkResource().openStream(path); + } + return inputStream; + } + +} diff --git a/src/main/java/com/fr/plugin/zk/stream/excel/provider/ExportProvider.java b/src/main/java/com/fr/plugin/zk/stream/excel/provider/ExportProvider.java new file mode 100644 index 0000000..e578a5d --- /dev/null +++ b/src/main/java/com/fr/plugin/zk/stream/excel/provider/ExportProvider.java @@ -0,0 +1,73 @@ +package com.fr.plugin.zk.stream.excel.provider; + +import com.fr.decision.fun.impl.BaseHttpHandler; +import com.fr.decision.webservice.Response; +import com.fr.decision.webservice.v10.user.UserService; +import com.fr.intelli.record.Focus; +import com.fr.intelli.record.Original; +import com.fr.log.FineLoggerFactory; +import com.fr.plugin.transform.ExecuteFunctionRecord; +import com.fr.plugin.transform.FunctionRecorder; +import com.fr.plugin.zk.stream.excel.dto.ExportDto; +import com.fr.plugin.zk.stream.excel.export.ExportAction; +import com.fr.plugin.zk.stream.excel.util.Constants; +import com.fr.plugin.zk.stream.excel.util.ExportUtil; +import com.fr.record.analyzer.EnableMetrics; +import com.fr.stable.StringUtils; +import com.fr.third.springframework.web.bind.annotation.RequestMethod; +import com.fr.web.utils.WebUtils; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +@EnableMetrics +@FunctionRecorder +public class ExportProvider extends BaseHttpHandler { + + @Override + public RequestMethod getMethod() { + return RequestMethod.POST; + } + + @Override + public String getPath() { + return "/stream/export"; + } + + @Override + public boolean isPublic() { + return false; + } + + @Focus(id = Constants.PLUGIN_ID, text = "Async export stream excel", source = Original.PLUGIN) + @ExecuteFunctionRecord + @Override + public void handle(HttpServletRequest req, HttpServletResponse res) throws Exception { + res.setContentType("application/json; charset=UTF-8"); + String username = ExportUtil.getLoginUserAndResponse(req, res); + if (StringUtils.isBlank(username)) { + return; + } + try { + String body = ExportUtil.requestBody2JsonString(req); + ExportDto exportDto = ExportUtil.OBJECT_MAPPER.readValue(body, ExportDto.class); + exportDto.setUsername(username); + exportDto.setFullPath(req); + ExecutorService service = Executors.newSingleThreadExecutor(); + service.execute(new ExportAction(req, exportDto)); + service.shutdown(); + Response response = Response.success(); + response.status(200); + WebUtils.printAsString(res, ExportUtil.OBJECT_MAPPER.writeValueAsString(response)); + } catch (Exception e) { + FineLoggerFactory.getLogger().error("获取参数失败:" + e.getMessage(), e); + Response response = Response.error(500, "", "获取参数失败,请填写正确的参数!"); + WebUtils.printAsString(res, ExportUtil.OBJECT_MAPPER.writeValueAsString(response)); + } + + } + + +} diff --git a/src/main/java/com/fr/plugin/zk/stream/excel/provider/MessageProvider.java b/src/main/java/com/fr/plugin/zk/stream/excel/provider/MessageProvider.java new file mode 100644 index 0000000..53a31b4 --- /dev/null +++ b/src/main/java/com/fr/plugin/zk/stream/excel/provider/MessageProvider.java @@ -0,0 +1,53 @@ +package com.fr.plugin.zk.stream.excel.provider; + +import com.fr.decision.fun.impl.BaseHttpHandler; +import com.fr.log.FineLoggerFactory; +import com.fr.plugin.zk.stream.excel.db.entity.ExcelExportEntity; +import com.fr.plugin.zk.stream.excel.db.service.ExcelExportService; +import com.fr.plugin.zk.stream.excel.util.ExportUtil; +import com.fr.stable.StringUtils; +import com.fr.third.springframework.web.bind.annotation.RequestMethod; +import com.fr.web.utils.WebUtils; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class MessageProvider extends BaseHttpHandler { + + @Override + public RequestMethod getMethod() { + return RequestMethod.GET; + } + + @Override + public String getPath() { + return "/stream/message"; + } + + @Override + public boolean isPublic() { + return false; + } + + @Override + public void handle(HttpServletRequest req, HttpServletResponse res) throws Exception { + String username = ExportUtil.getLoginUserAndResponse(req, res); + if (StringUtils.isBlank(username)) { + return; + } + String id = WebUtils.getHTTPRequestParameter(req, "id"); + try { + ExcelExportEntity entity = ExcelExportService.getSingleton().queryById(id); + if (entity == null) { + WebUtils.printAsString(res, "找不到对应的消息"); + } else { + res.setContentType("text/html; charset=gb2312"); + WebUtils.printAsString(res, entity.getMessage()!=null?entity.getMessage():"无错误信息"); + } + } catch (Exception e) { + WebUtils.printAsString(res, "找不到对应的消息"); + FineLoggerFactory.getLogger().error(e.getMessage(),e); + } + } + +} diff --git a/src/main/java/com/fr/plugin/zk/stream/excel/util/Constants.java b/src/main/java/com/fr/plugin/zk/stream/excel/util/Constants.java new file mode 100644 index 0000000..756b438 --- /dev/null +++ b/src/main/java/com/fr/plugin/zk/stream/excel/util/Constants.java @@ -0,0 +1,9 @@ +package com.fr.plugin.zk.stream.excel.util; + +public interface Constants { + /*** + * 插件id + */ + String PLUGIN_ID = "com.fr.plugin.zk.jsd8101.excel"; + +} diff --git a/src/main/java/com/fr/plugin/zk/stream/excel/util/ExportUtil.java b/src/main/java/com/fr/plugin/zk/stream/excel/util/ExportUtil.java new file mode 100644 index 0000000..188883d --- /dev/null +++ b/src/main/java/com/fr/plugin/zk/stream/excel/util/ExportUtil.java @@ -0,0 +1,115 @@ +package com.fr.plugin.zk.stream.excel.util; + +import com.fr.decision.webservice.Response; +import com.fr.decision.webservice.v10.user.UserService; +import com.fr.log.FineLoggerFactory; +import com.fr.stable.CommonCodeUtils; +import com.fr.stable.StringUtils; +import com.fr.third.fasterxml.jackson.core.JsonProcessingException; +import com.fr.third.fasterxml.jackson.databind.ObjectMapper; +import com.fr.web.utils.WebUtils; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Iterator; +import java.util.Map; + +/** + * @author fr.open + * @Project name finereport-gradle + * @description: + * @created on 2021-06-24 16:01. + * @Modified By + */ +public class ExportUtil { + + public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + public static String requestBody2JsonString(HttpServletRequest req) throws IOException { + String data = ""; + StringBuffer stringBuffer = new StringBuffer(); + BufferedReader reader = new BufferedReader(new InputStreamReader(req.getInputStream(), StandardCharsets.UTF_8)); + while (null != (data = reader.readLine())) { + stringBuffer.append(data); + } + return stringBuffer.toString(); + } + + public static String buildServerURL(HttpServletRequest req) { + StringBuffer serverURIBuf = new StringBuffer(); + serverURIBuf.append("http"); + serverURIBuf.append("://"); + serverURIBuf.append("127.0.0.1"); + serverURIBuf.append(':'); + serverURIBuf.append(req.getServerPort()); + return serverURIBuf.toString(); + } + + public static String writeToJson(Object o) { + try { + return OBJECT_MAPPER.writeValueAsString(o); + } catch (JsonProcessingException e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + return ""; + } + + public static String getLoginUserAndResponse(HttpServletRequest req, HttpServletResponse res) { + try { + res.setContentType("application/json; charset=UTF-8"); + String username = UserService.getInstance().getUserByRequestCookie(req).getUserName(); + if (StringUtils.isBlank(username)) { + Response response = Response.error(500, "", "用户未登录!"); + WebUtils.printAsString(res, ExportUtil.OBJECT_MAPPER.writeValueAsString(response)); + return null; + } + return username; + } catch (Exception e) { + res.setContentType("application/json; charset=UTF-8"); + FineLoggerFactory.getLogger().error(e.getMessage(), e); + Response response = Response.error(500, "", "用户未登录!"); + try { + WebUtils.printAsString(res, ExportUtil.OBJECT_MAPPER.writeValueAsString(response)); + } catch (Exception exception) { + } + return null; + } + } + + public static String appendUrlPara(String url,Map maps) { + if (maps != null) { + StringBuilder builder = new StringBuilder(); + builder.append(url); + Iterator iterator = maps.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry mapEntry = (Map.Entry) iterator.next(); + builder.append('&'); + builder.append(encodeString(mapEntry.getKey().toString(),true)); + builder.append('='); + builder.append(encodeString(mapEntry.getValue().toString(),true)); + } + return builder.toString(); + } + return url; + } + + public static String encodeString(String var1, boolean var2) { + String var3 = CommonCodeUtils.cjkEncode(var1); + if (!var2) { + return var3; + } else { + try { + return URLEncoder.encode(var3, "UTF-8"); + } catch (UnsupportedEncodingException var5) { + return var3; + } + } + } + +} diff --git a/使用说明文档.docx b/使用说明文档.docx new file mode 100644 index 0000000000000000000000000000000000000000..479b2e8d391c93091e09cdf65c211b3207c06e28 GIT binary patch literal 31936 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
3vZOST%My#i;=>HhqsqhZ&*2D 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