Jelajahi Sumber

优化对报表数据量的限制策略,通过数据条数而非页数进行判断

sunyj 8 tahun lalu
induk
melakukan
97e0ed1b4d

+ 14 - 22
src/main/java/com/uas/report/controller/PrintController.java

@@ -39,19 +39,14 @@ import com.uas.report.util.ReportUtils;
 @RequestMapping("/print")
 public class PrintController {
 
-	private static Logger logger = LoggerFactory.getLogger(PrintController.class);
-
-	/**
-	 * 报表支持的最大页数,无论是下载excel、pdf还是预览,超过该数值,均不提供服务
-	 */
-	private static final Integer MAX_PAGE_SIZE = 1000;
-
 	@Autowired
 	private PrintService printService;
 
 	@Autowired
 	private FileService fileService;
 
+	private static Logger logger = LoggerFactory.getLogger(PrintController.class);
+
 	/**
 	 * 为UAS系统打印提供服务, 根据printType进行预览、打印、下载pdf、下载纯数据excel等操作
 	 * 
@@ -136,6 +131,9 @@ public class PrintController {
 			String otherParameters, String exportFileType, Boolean flush, HttpServletRequest request,
 			HttpServletResponse response) {
 		ReportUtils.checkParameters(userName, reportName);
+		if (printService.overload(userName, profile, reportName, whereCondition)) {
+			throw new ReportException("数据量过大,无法提供服务");
+		}
 		String masterOfJrxml = printService.getMasterOfJrxml(userName, reportName);
 		if (StringUtils.isEmpty(exportFileType)) {
 			exportFileType = ReportConstants.PDF_FILE_TYPE;
@@ -225,6 +223,9 @@ public class PrintController {
 			final String whereCondition, final String otherParameters, Integer pageIndex, Boolean flush,
 			HttpServletRequest request, HttpServletResponse response) {
 		ReportUtils.checkParameters(userName, reportName);
+		if (printService.overload(userName, profile, reportName, whereCondition)) {
+			throw new ReportException("数据量过大,无法提供服务");
+		}
 		String masterOfJrxml = printService.getMasterOfJrxml(userName, reportName);
 		Map<String, Object> result = new HashMap<>();
 
@@ -296,25 +297,16 @@ public class PrintController {
 	 */
 	@RequestMapping(value = "/pdfData")
 	@ResponseBody
-	public Map<String, Object> getPdfPath(final String userName, final String profile, final String reportName,
+	public Map<String, Object> getPdfData(final String userName, final String profile, final String reportName,
 			final String whereCondition, final String otherParameters, HttpServletRequest request,
 			HttpServletResponse response) {
 		ReportUtils.checkParameters(userName, reportName);
-
-		// 通过预览第一页,获取总页数
-		// TODO 需重写判断页数的方法
-		Map<String, Object> result = printService.preview(userName, profile, reportName, whereCondition,
-				otherParameters, 1);
-		Integer pageSize = null;
-		if (result != null && result.containsKey("data") && result.containsKey("pageSize")) {
-			pageSize = (Integer) result.get("pageSize");
-		}
-		if (pageSize == null) {
-			throw new ReportException("获取预览数据失败");
-		}
-		if (pageSize > MAX_PAGE_SIZE) {
+		Map<String, Object> result = new HashMap<>();
+		// 判断是否过载
+		if (printService.overload(userName, profile, reportName, whereCondition)) {
 			result.put("data", "");
-			result.put("overload", "true");
+			result.put("pageSize", 0);
+			result.put("overload", true);
 		} else {
 			result = printService.preview(userName, profile, reportName, whereCondition, otherParameters, null);
 			result.put("overload", false);

+ 17 - 2
src/main/java/com/uas/report/service/PrintService.java

@@ -78,8 +78,8 @@ public interface PrintService {
 	/**
 	 * 获取模板对应的账套
 	 * 
-	 * 1. 处理B2B账套,如果企业没有自己的模板,则使用B2B的标准模板(返回B2B标准账套)
-	 * 2. 有些主账套和子帐套共用主账套的模板,需要进行处理,返回主账套的名称
+	 * 1. 处理B2B账套,如果企业没有自己的模板,则使用B2B的标准模板(返回B2B标准账套) 2.
+	 * 有些主账套和子帐套共用主账套的模板,需要进行处理,返回主账套的名称
 	 * 
 	 * @param userName
 	 *            账套名称
@@ -89,4 +89,19 @@ public interface PrintService {
 	 */
 	public String getMasterOfJrxml(String userName, String reportName);
 
+	/**
+	 * 判断该模板在当前条件下的结果数目是否超出限制
+	 * 
+	 * @param userName
+	 *            不为null;当前账套用户名
+	 * @param profile
+	 *            用于标识请求源(B2C、B2B)是正式、测试还是开发版本:prod、test、dev
+	 * @param reportName
+	 *            不为null;需要导出的报表的名称,不带任何后缀(如导出采购单,即为"Purchase")
+	 * @param whereCondition
+	 *            可为null;where之后的条件(包括where)
+	 * @return 结果数目超出限制,返回true
+	 */
+	public boolean overload(String userName, String profile, String reportName, String whereCondition);
+
 }

+ 97 - 1
src/main/java/com/uas/report/service/impl/PrintServiceImpl.java

@@ -82,6 +82,11 @@ import net.sf.jasperreports.export.WriterExporterOutput;
 @Service
 public class PrintServiceImpl implements PrintService {
 
+	/**
+	 * 报表支持的最大数据库记录数目
+	 */
+	private static final Integer MAX_RECODR_SIZE = 100000;
+
 	@Autowired
 	private FileService fileService;
 
@@ -344,7 +349,7 @@ public class PrintServiceImpl implements PrintService {
 			}
 			// 如果查询语句中含有where条件,则可能有用到一些参数,需将其替换掉
 			else if (queryString.toUpperCase().contains("WHERE")) {
-				queryString = queryString.substring(0, queryString.indexOf("WHERE")) + "where rownum = 0";
+				queryString = queryString.substring(0, queryString.toUpperCase().indexOf("WHERE")) + "where rownum = 0";
 			}
 			List<String> columnNames = getColumnNames(connection, queryString);
 			if (CollectionUtils.isEmpty(columnNames)) {
@@ -632,4 +637,95 @@ public class PrintServiceImpl implements PrintService {
 		return userName;
 	}
 
+	@Override
+	public boolean overload(String userName, String profile, String reportName, String whereCondition) {
+		DataSource dataSource = MasterManager.getDataSource(userName, profile);
+		if (dataSource == null) {
+			throw new ReportException("获取数据源失败");
+		}
+
+		String masterOfJrxml = getMasterOfJrxml(userName, reportName);
+		String jrxmlFilePath = fileService.getJrxmlFilePath(masterOfJrxml, reportName);
+		Connection connection = null;
+		try {
+			logger.info("dataSource.getConnection..." + userName);
+			connection = dataSource.getConnection();
+			logger.info("dataSource.getConnection done..." + userName);
+
+			File jrxmlFile = new File(jrxmlFilePath);
+			// 报表模板不存在
+			if (!jrxmlFile.exists()) {
+				// 替换windows下路径中的双反斜杠为单斜杠
+				throw new ReportException("未发现模板文件:" + jrxmlFilePath.replaceAll("\\\\", "/"));
+			}
+
+			// 因为子报表数据量较小,而且其参数来自主报表,无法简单地进行判断,所以不判断子报表是否过载
+			int count = getCount(jrxmlFilePath, whereCondition, connection);
+			logger.info("count... " + count);
+			return count >= MAX_RECODR_SIZE;
+		} catch (Exception e) {
+			throw new ReportException(e).setDetailedMessage(e);
+		} finally {
+			if (connection != null) {
+				try {
+					connection.close();
+				} catch (SQLException e) {
+					throw new ReportException(e).setDetailedMessage(e);
+				}
+			}
+		}
+	}
+
+	/**
+	 * 获取该模板在当前条件下的结果数目
+	 * 
+	 * @param jrxmlFilePath
+	 * @param whereCondition
+	 * @param connection
+	 */
+	private int getCount(String jrxmlFilePath, String whereCondition, Connection connection) {
+		XMLWriter xmlWriter = null;
+		try {
+			SAXReader saxReader = new SAXReader();
+			Document document = saxReader.read(new File(jrxmlFilePath));
+			Element rootElement = document.getRootElement();
+			// 查询语句
+			Element queryStringElement = rootElement.element("queryString");
+			String queryString = queryStringElement.getText();
+			// 如果查询语句中含有WHERE_CONDITION参数,需将其替换掉
+			if (queryString.contains("$P!{WHERE_CONDITION}") && !StringUtils.isEmpty(whereCondition)) {
+				queryString = queryString.replace("$P!{WHERE_CONDITION}", whereCondition);
+			}
+			return getCount(connection, queryString);
+		} catch (DocumentException | SQLException e) {
+			throw new ReportException(e).setDetailedMessage(e);
+		} finally {
+			if (xmlWriter != null) {
+				try {
+					xmlWriter.close();
+				} catch (IOException e) {
+					throw new ReportException(e).setDetailedMessage(e);
+				}
+			}
+		}
+	}
+
+	/**
+	 * 获取当前查询语句的结果数目
+	 * 
+	 * @param connection
+	 * @param sql
+	 * @return
+	 * @throws SQLException
+	 */
+	private int getCount(Connection connection, String sql) throws SQLException {
+		sql = sql.toLowerCase();
+		sql = "select count(1) " + sql.substring(sql.indexOf("from"));
+		PreparedStatement preparedStatement = connection.prepareStatement(sql);
+		ResultSet resultSet = preparedStatement.executeQuery();
+		resultSet.next();
+		int count = resultSet.getInt(1);
+		return count;
+	}
+
 }