报表模板新增导入导出接口

This commit is contained in:
gaoqr
2024-12-04 11:49:37 +08:00
parent 4b6d958303
commit bf4e04738d
4 changed files with 122 additions and 36 deletions
@@ -26,7 +26,6 @@ import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream; import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.TerminalNode; import org.antlr.v4.runtime.tree.TerminalNode;
import org.apache.http.HttpStatus; import org.apache.http.HttpStatus;
import org.apache.ibatis.annotations.Param;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@@ -145,7 +144,7 @@ public class ReportTemplateController {
@Operation(summary = "校验表达式") @Operation(summary = "校验表达式")
@Parameter(name = "content", description = "表达式", required = true) @Parameter(name = "content", description = "表达式", required = true)
@PreAuthorize("@ss.hasPermission('report:design')") @PreAuthorize("@ss.hasPermission('report:design')")
public List<ErrorInfo> scriptValidation(@Param("content") @NotEmpty(message = "表达式不能为空") String content) { public List<ErrorInfo> scriptValidation(@RequestParam("content") @NotEmpty(message = "表达式不能为空") String content) {
content = StringUtils.decode(content); content = StringUtils.decode(content);
ANTLRInputStream antlrInputStream = new ANTLRInputStream(content); ANTLRInputStream antlrInputStream = new ANTLRInputStream(content);
ReportParserLexer lexer = new ReportParserLexer(antlrInputStream); ReportParserLexer lexer = new ReportParserLexer(antlrInputStream);
@@ -168,7 +167,7 @@ public class ReportTemplateController {
@Operation(summary = "解析表达式中的数据集") @Operation(summary = "解析表达式中的数据集")
@Parameter(name = "expr", description = "表达式", required = true) @Parameter(name = "expr", description = "表达式", required = true)
@PreAuthorize("@ss.hasPermission('report:design')") @PreAuthorize("@ss.hasPermission('report:design')")
public Map<String, String> parseDatasetName(@Param("expr") @NotEmpty(message = "表达式不能为空") String expr) { public Map<String, String> parseDatasetName(@RequestParam("expr") @NotEmpty(message = "表达式不能为空") String expr) {
ANTLRInputStream antlrInputStream = new ANTLRInputStream(expr); ANTLRInputStream antlrInputStream = new ANTLRInputStream(expr);
ReportParserLexer lexer = new ReportParserLexer(antlrInputStream); ReportParserLexer lexer = new ReportParserLexer(antlrInputStream);
CommonTokenStream tokenStream = new CommonTokenStream(lexer); CommonTokenStream tokenStream = new CommonTokenStream(lexer);
@@ -186,4 +185,19 @@ public class ReportTemplateController {
} }
return result; return result;
} }
@GetMapping("/xml/{id}")
@Operation(summary = "导出模版")
@Parameter(name = "id", description = "模板id", required = true)
@PreAuthorize("@ss.hasPermission('report:design')")
public void exportTemplate(@PathVariable("id") Long templateId, HttpServletResponse response) {
templateService.exportTemplate(templateId, response);
}
@PutMapping("/xml")
@Operation(summary = "导入模版")
@PreAuthorize("@ss.hasPermission('report:design')")
public void importTemplate(@RequestParam("name") @NotEmpty(message = "模版名称不能为空") String name, @RequestParam("file") MultipartFile file) {
templateService.importTemplate(name, file);
}
} }
@@ -4,12 +4,9 @@ import com.baomidou.mybatisplus.annotation.*;
import com.cf.imes.framework.mybatis.core.dataobject.BaseDO; import com.cf.imes.framework.mybatis.core.dataobject.BaseDO;
import com.cf.imes.framework.mybatis.core.type.CompressStringTypeHandler; import com.cf.imes.framework.mybatis.core.type.CompressStringTypeHandler;
import com.cf.imes.framework.mybatis.core.type.JsonLongSetTypeHandler; import com.cf.imes.framework.mybatis.core.type.JsonLongSetTypeHandler;
import com.cf.imes.framework.mybatis.core.type.LongListTypeHandler;
import com.cf.imes.module.report.dal.dataobject.datasource.ReportDatasourceDO;
import com.cf.imes.module.report.enums.template.ReportTemplateTypeEnum; import com.cf.imes.module.report.enums.template.ReportTemplateTypeEnum;
import lombok.*; import lombok.*;
import java.util.List;
import java.util.Set; import java.util.Set;
/** /**
@@ -56,12 +53,6 @@ public class ReportTemplateDO extends BaseDO {
@TableField(fill = FieldFill.INSERT_UPDATE) @TableField(fill = FieldFill.INSERT_UPDATE)
private Long organId; private Long organId;
/**
* 数据源
*/
@TableField(exist = false)
private List<ReportDatasourceDO> datasource;
/** /**
* 数据源id列表 * 数据源id列表
*/ */
@@ -7,6 +7,7 @@ import com.cf.imes.module.report.controller.admin.template.vo.ReportTemplateReqV
import com.cf.imes.module.report.controller.admin.template.vo.ReportTemplateRespVO; import com.cf.imes.module.report.controller.admin.template.vo.ReportTemplateRespVO;
import com.cf.imes.module.report.controller.admin.template.vo.ReportTemplateSaveReqVO; import com.cf.imes.module.report.controller.admin.template.vo.ReportTemplateSaveReqVO;
import com.cf.imes.module.report.dal.dataobject.template.ReportTemplateDO; import com.cf.imes.module.report.dal.dataobject.template.ReportTemplateDO;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid; import javax.validation.Valid;
@@ -75,7 +76,7 @@ public interface ReportTemplateService {
/** /**
* 模板预览 * 模板预览
* *
* @param params * @param reqVO
* @return * @return
*/ */
HtmlReport preview(ReportTemplatePreviewReqVO reqVO); HtmlReport preview(ReportTemplatePreviewReqVO reqVO);
@@ -89,8 +90,24 @@ public interface ReportTemplateService {
* 模版预览下载 * 模版预览下载
* *
* @param type 文件类型 * @param type 文件类型
* @param params * @param reqVO
* @param response * @param response
*/ */
void download(String type, ReportTemplatePreviewReqVO reqVO, HttpServletResponse response); void download(String type, ReportTemplatePreviewReqVO reqVO, HttpServletResponse response);
/**
* 模板导出
*
* @param templateId
* @param response
*/
void exportTemplate(Long templateId, HttpServletResponse response);
/**
* 模版导入
*
* @param name
* @param file
*/
void importTemplate(String name, MultipartFile file);
} }
@@ -2,9 +2,14 @@ package com.cf.imes.module.report.service.template;
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.CharsetUtil; import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.URLUtil; import cn.hutool.core.util.URLUtil;
import cn.hutool.http.ContentType;
import cn.hutool.http.Header;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.bstek.common.exception.ReportDesignException; import com.bstek.common.exception.ReportDesignException;
import com.bstek.designer.bean.PreviewParameters; import com.bstek.designer.bean.PreviewParameters;
@@ -47,11 +52,13 @@ import com.cf.imes.module.report.dal.mysql.datasource.ReportDatasourceMapper;
import com.cf.imes.module.report.dal.mysql.template.ReportTemplateMapper; import com.cf.imes.module.report.dal.mysql.template.ReportTemplateMapper;
import com.cf.imes.module.report.enums.template.ReportTemplateTypeEnum; import com.cf.imes.module.report.enums.template.ReportTemplateTypeEnum;
import com.cf.imes.module.report.utils.TenantParamsUtils; import com.cf.imes.module.report.utils.TenantParamsUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@@ -59,13 +66,14 @@ import java.io.ByteArrayInputStream;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
import static com.cf.imes.framework.common.exception.util.ServiceExceptionUtil.exception; import static com.cf.imes.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.cf.imes.module.report.enums.ErrorCodeConstants.TEMPLATE_BUILDIN_OPERATION_PERMISSION_ERROR; import static com.cf.imes.module.report.enums.ErrorCodeConstants.TEMPLATE_BUILDIN_OPERATION_PERMISSION_ERROR;
@@ -81,6 +89,7 @@ import static com.cf.imes.module.report.enums.ErrorCodeConstants.TEMPLATE_PREIVE
*/ */
@Service @Service
@Validated @Validated
@Slf4j
public class ReportTemplateServiceImpl implements ReportTemplateService { public class ReportTemplateServiceImpl implements ReportTemplateService {
@Resource @Resource
private ReportTemplateMapper templateMapper; private ReportTemplateMapper templateMapper;
@@ -92,6 +101,9 @@ public class ReportTemplateServiceImpl implements ReportTemplateService {
private ReportDatasetMapper datasetMapper; private ReportDatasetMapper datasetMapper;
private static final String UTF8_CHARSET = CharsetUtil.UTF_8; private static final String UTF8_CHARSET = CharsetUtil.UTF_8;
private static final String ATTACHMENT_PREFIX = "attachment; filename=";
private static final String DATASETIDS_FIELD_NAME = "datasetIds";
private static final String DATASOURCEIDS_FIELD_NAME = "datasourceIds";
@Resource @Resource
private TenantParamsUtils tenantParamsUtils; private TenantParamsUtils tenantParamsUtils;
@@ -360,7 +372,7 @@ public class ReportTemplateServiceImpl implements ReportTemplateService {
if (ObjectUtil.isNotNull(queryMap)) { if (ObjectUtil.isNotNull(queryMap)) {
for (Map.Entry<String, Object> entry : queryMap.entrySet()) { for (Map.Entry<String, Object> entry : queryMap.entrySet()) {
String key = entry.getKey(); String key = entry.getKey();
if (ObjectUtils.isEmpty(entry.getValue()) && ObjectUtil.notEqual(key, "datasetIds") && ObjectUtil.notEqual(key, "datasourceIds")) { if (ObjectUtils.isEmpty(entry.getValue()) && ObjectUtil.notEqual(key, DATASETIDS_FIELD_NAME) && ObjectUtil.notEqual(key, DATASOURCEIDS_FIELD_NAME)) {
throw ServiceExceptionUtil.exception(TEMPLATE_PREIVEW_QUERYPARAM_EMPTY, key); throw ServiceExceptionUtil.exception(TEMPLATE_PREIVEW_QUERYPARAM_EMPTY, key);
} }
} }
@@ -405,12 +417,14 @@ public class ReportTemplateServiceImpl implements ReportTemplateService {
// 创建下载文件 // 创建下载文件
ProducerEnum p = ProducerEnum.PDF; ProducerEnum p = ProducerEnum.PDF;
String downFileName = com.bstek.common.utils.StringUtils.randomFileName() + com.bstek.common.utils.StringUtils.getFileSuffix(p); String downFileName = com.bstek.common.utils.StringUtils.randomFileName() + com.bstek.common.utils.StringUtils.getFileSuffix(p);
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(downFileName, UTF8_CHARSET)); response.setHeader(Header.CONTENT_DISPOSITION.getValue(), ATTACHMENT_PREFIX + URLEncoder.encode(downFileName, UTF8_CHARSET));
response.setHeader("code", "20000"); response.setHeader("code", "20000");
ExportUtils.export(out, report, p); ExportUtils.export(out, report, p);
long end = System.currentTimeMillis(); long end = System.currentTimeMillis();
ToolUtils.logToConsole("获取打印文件耗时:" + (end - start) + "ms"); ToolUtils.logToConsole("获取打印文件耗时:" + (end - start) + "ms");
} catch (Exception ex) { } catch (Exception ex) {
log.error("模版预览打印异常", ex);
throw new ReportException(ex); throw new ReportException(ex);
} }
} }
@@ -435,13 +449,81 @@ public class ReportTemplateServiceImpl implements ReportTemplateService {
// 创建下载文件 // 创建下载文件
ProducerEnum p = ProducerEnum.valueOf(type.toUpperCase()); ProducerEnum p = ProducerEnum.valueOf(type.toUpperCase());
String downFileName = com.bstek.common.utils.StringUtils.randomFileName() + com.bstek.common.utils.StringUtils.getFileSuffix(p); String downFileName = com.bstek.common.utils.StringUtils.randomFileName() + com.bstek.common.utils.StringUtils.getFileSuffix(p);
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(downFileName, UTF8_CHARSET)); response.setHeader(Header.CONTENT_DISPOSITION.getValue(), ATTACHMENT_PREFIX + URLEncoder.encode(downFileName, UTF8_CHARSET));
response.setHeader("code", "20000"); response.setHeader("code", "20000");
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
ExportUtils.export(out, report, p); ExportUtils.export(out, report, p);
long end = System.currentTimeMillis(); long end = System.currentTimeMillis();
ToolUtils.logToConsole("下载 " + type + " 报表耗时:" + (end - start) + "ms"); ToolUtils.logToConsole("下载 " + type + " 报表耗时:" + (end - start) + "ms");
} catch (Exception ex) { } catch (Exception ex) {
log.error("模版预览下载文件异常", ex);
throw new ReportException(ex);
}
}
@Override
public void exportTemplate(Long templateId, HttpServletResponse response) {
ReportTemplateDO reportTemplateDO = validateTemplateExists(templateId);
if (ObjectUtil.isNull(reportTemplateDO)) {
throw exception(TEMPLATE_NOT_EXISTS);
}
PrintWriter writer = null;
try {
// 模版关键信息
JSONObject exportObj = new JSONObject();
exportObj.put("content", reportTemplateDO.getContent());
Set<Long> datasourceIds = reportTemplateDO.getDatasourceIds();
exportObj.put(DATASOURCEIDS_FIELD_NAME, CollUtil.isNotEmpty(datasourceIds) ? datasourceIds : Set.of());
Set<Long> datasetIds = reportTemplateDO.getDatasetIds();
exportObj.put(DATASETIDS_FIELD_NAME, CollUtil.isNotEmpty(datasetIds) ? datasourceIds : Set.of());
// 设置响应的内容类型为 application/json
response.setContentType(ContentType.JSON.getValue());
response.setCharacterEncoding(UTF8_CHARSET);
response.setHeader(Header.CONTENT_DISPOSITION.getValue(), ATTACHMENT_PREFIX + URLEncoder.encode(String.format("%s.json", reportTemplateDO.getName()), UTF8_CHARSET));
// 写入响应的输出流
writer = response.getWriter();
writer.print(exportObj.toJSONString());
writer.flush();
} catch (Exception ex) {
log.error("模板导出异常", ex);
throw new ReportException(ex);
} finally {
IoUtil.close(writer);
}
}
@Override
public void importTemplate(String name, MultipartFile file) {
try {
// 读取文件json
String templateStr = IoUtil.read(file.getInputStream(), UTF8_CHARSET);
JSONObject jsonObject = JSON.parseObject(templateStr);
boolean isSuperAdmin = SecurityFrameworkUtils.isSuperAdmin();
ReportTemplateSaveReqVO createReqVO = new ReportTemplateSaveReqVO();
createReqVO.setName(name);
// 校验模板名唯一性
validateTemplateNameUnique(createReqVO, isSuperAdmin);
// 复制信息
createReqVO.setContent(jsonObject.getString("content"));
createReqVO.setDatasetIds(jsonObject.getJSONArray(DATASETIDS_FIELD_NAME).stream().map(s -> Long.parseLong(s.toString())).collect(Collectors.toSet()));
createReqVO.setDatasourceIds(jsonObject.getJSONArray(DATASOURCEIDS_FIELD_NAME).stream().map(s -> Long.parseLong(s.toString())).collect(Collectors.toSet()));
// 新增模板
ReportTemplateDO template = BeanUtils.toBean(createReqVO, ReportTemplateDO.class);
// 超管创建的作为内置模板
if (isSuperAdmin) {
template.setType(ReportTemplateTypeEnum.SYSTEM);
} else {
template.setType(ReportTemplateTypeEnum.CUSTOM);
}
templateMapper.insert(template);
} catch (ServiceException sex) {
throw sex;
} catch (Exception ex) {
log.error("模板导入异常", ex);
throw new ReportException(ex); throw new ReportException(ex);
} }
} }
@@ -474,22 +556,4 @@ public class ReportTemplateServiceImpl implements ReportTemplateService {
parameters.put("pageIndex", params.getPageIndex()); parameters.put("pageIndex", params.getPageIndex());
return reportDefinition; return reportDefinition;
} }
/**
* 模板生成文件类型校验
*
* @param type
*/
private void validateProducerType(String type) {
boolean match = false;
for (ProducerEnum producerEnum : ProducerEnum.values()) {
if (Objects.equals(producerEnum.name(), type.toUpperCase())) {
match = true;
break;
}
}
if (!match) {
throw new ReportException("类型错误,请检查是否PDF/WORD/EXCEL");
}
}
} }