Java报表系统案例

wen java案例 3

本文目录导读:

Java报表系统案例

  1. 数据库设计
  2. 后端核心代码
  3. 前端页面(HTML + ECharts)
  4. JasperReports模板配置
  5. 项目配置
  6. 核心功能演示效果
  7. 优化建议
  8. 扩展功能

下面给您一个完整的Java报表系统案例,从技术选型到核心代码实现,涵盖实际项目中最常用的技术方案。

需求:为企业开发一个销售报表系统,实现销售数据的可视化展示与导出功能。

技术栈

  • 后端:Spring Boot 2.x + MyBatis-Plus
  • 报表引擎:JasperReports(服务端生成PDF/Excel)
  • ECharts(前端可视化图表)
  • 数据库:MySQL
  • 工具:Maven

数据库设计

-- 销售订单表
CREATE TABLE `sales_order` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `order_no` varchar(32) NOT NULL COMMENT '订单号',
  `product_name` varchar(100) NOT NULL COMMENT '商品名称',
  `category` varchar(50) DEFAULT NULL COMMENT '商品分类',
  `quantity` int(11) NOT NULL COMMENT '销售数量',
  `amount` decimal(10,2) NOT NULL COMMENT '销售金额',
  `salesman` varchar(50) DEFAULT NULL COMMENT '销售员',
  `order_date` date NOT NULL COMMENT '订单日期',
  PRIMARY KEY (`id`),
  KEY `idx_order_date` (`order_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

后端核心代码

Entity实体类

@Data
@TableName("sales_order")
public class SalesOrder {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String orderNo;
    private String productName;
    private String category;
    private Integer quantity;
    private BigDecimal amount;
    private String salesman;
    private Date orderDate;
}

Mapper层

@Mapper
public interface SalesOrderMapper extends BaseMapper<SalesOrder> {
    // 按日期区间汇总销售额
    @Select("SELECT DATE_FORMAT(order_date, '%Y-%m-%d') as date, " +
            "SUM(amount) as totalAmount, SUM(quantity) as totalQuantity " +
            "FROM sales_order " +
            "WHERE order_date BETWEEN #{startDate} AND #{endDate} " +
            "GROUP BY DATE_FORMAT(order_date, '%Y-%m-%d')")
    List<Map<String, Object>> getDailySalesSummary(@Param("startDate") String startDate, 
                                                   @Param("endDate") String endDate);
    // 按商品分类统计
    @Select("SELECT category, COUNT(*) as orderCount, " +
            "SUM(amount) as totalAmount " +
            "FROM sales_order " +
            "WHERE order_date BETWEEN #{startDate} AND #{endDate} " +
            "GROUP BY category")
    List<Map<String, Object>> getCategorySummary(@Param("startDate") String startDate, 
                                                 @Param("endDate") String endDate);
}

Service层

@Service
public class ReportService {
    @Autowired
    private SalesOrderMapper salesOrderMapper;
    // 获取销售趋势数据(用于ECharts)
    public Map<String, Object> getSalesTrend(String startDate, String endDate) {
        List<Map<String, Object>> dailyData = salesOrderMapper.getDailySalesSummary(startDate, endDate);
        List<String> dates = new ArrayList<>();
        List<BigDecimal> amounts = new ArrayList<>();
        for (Map<String, Object> item : dailyData) {
            dates.add(item.get("date").toString());
            amounts.add(new BigDecimal(item.get("totalAmount").toString()));
        }
        Map<String, Object> result = new HashMap<>();
        result.put("dates", dates);
        result.put("amounts", amounts);
        return result;
    }
    // 获取分类占比数据
    public List<Map<String, Object>> getCategoryData(String startDate, String endDate) {
        return salesOrderMapper.getCategorySummary(startDate, endDate);
    }
    // 导出PDF报表
    public byte[] exportPdfReport(String startDate, String endDate) throws Exception {
        // 获取数据
        List<SalesOrder> orders = salesOrderMapper.selectList(
            new LambdaQueryWrapper<SalesOrder>()
                .between(SalesOrder::getOrderDate, startDate, endDate)
        );
        // 加载JasperReports模板
        InputStream inputStream = getClass().getResourceAsStream("/reports/sales_report.jrxml");
        JasperReport jasperReport = JasperCompileManager.compileReport(inputStream);
        // 填充数据
        JRBeanCollectionDataSource dataSource = new JRBeanCollectionDataSource(orders);
        Map<String, Object> parameters = new HashMap<>();
        parameters.put("startDate", startDate);
        parameters.put("endDate", endDate);
        parameters.put("totalAmount", orders.stream()
            .map(SalesOrder::getAmount)
            .reduce(BigDecimal.ZERO, BigDecimal::add));
        JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, dataSource);
        // 导出为PDF
        return JasperExportManager.exportReportToPdf(jasperPrint);
    }
    // 导出Excel
    public void exportExcel(String startDate, String endDate, HttpServletResponse response) throws Exception {
        List<SalesOrder> orders = salesOrderMapper.selectList(
            new LambdaQueryWrapper<SalesOrder>()
                .between(SalesOrder::getOrderDate, startDate, endDate)
        );
        // 使用EasyExcel
        EasyExcel.write(response.getOutputStream(), SalesOrder.class)
            .sheet("销售报表")
            .doWrite(orders);
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        String fileName = URLEncoder.encode("销售报表", "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
    }
}

Controller层

@RestController
@RequestMapping("/api/report")
public class ReportController {
    @Autowired
    private ReportService reportService;
    // 获取销售趋势JSON
    @GetMapping("/sales-trend")
    public Result<Map<String, Object>> getSalesTrend(
            @RequestParam String startDate, 
            @RequestParam String endDate) {
        return Result.success(reportService.getSalesTrend(startDate, endDate));
    }
    // 获取分类数据
    @GetMapping("/category")
    public Result<List<Map<String, Object>>> getCategoryData(
            @RequestParam String startDate, 
            @RequestParam String endDate) {
        return Result.success(reportService.getCategoryData(startDate, endDate));
    }
    // 导出PDF
    @GetMapping("/export/pdf")
    public void exportPdf(@RequestParam String startDate, 
                          @RequestParam String endDate,
                          HttpServletResponse response) throws Exception {
        byte[] pdfBytes = reportService.exportPdfReport(startDate, endDate);
        response.setContentType("application/pdf");
        response.setHeader("Content-Disposition", "attachment; filename=sales_report.pdf");
        response.getOutputStream().write(pdfBytes);
    }
    // 导出Excel
    @GetMapping("/export/excel")
    public void exportExcel(@RequestParam String startDate, 
                            @RequestParam String endDate,
                            HttpServletResponse response) throws Exception {
        reportService.exportExcel(startDate, endDate, response);
    }
}

前端页面(HTML + ECharts)

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">销售报表系统</title>
    <script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
    <style>
        .container { display: flex; flex-wrap: wrap; padding: 20px; }
        .chart-container { 
            width: 48%; 
            height: 400px; 
            margin: 1%; 
            border: 1px solid #ddd; 
            border-radius: 8px;
            background: white;
            box-shadow: 0 2px 8px rgba(0,0,0,0.1);
        }
        .date-input { padding: 20px; }
        .btn { padding: 8px 16px; margin-right: 10px; }
    </style>
</head>
<body>
    <div class="date-input">
        开始日期:<input type="date" id="startDate" value="2024-01-01">
        结束日期:<input type="date" id="endDate" value="2024-12-31">
        <button class="btn" onclick="loadData()">查询</button>
        <button class="btn" onclick="exportPdf()">导出PDF</button>
        <button class="btn" onclick="exportExcel()">导出Excel</button>
    </div>
    <div class="container">
        <div id="salesTrend" class="chart-container"></div>
        <div id="categoryChart" class="chart-container"></div>
    </div>
    <script>
        // 初始化销售趋势图
        var trendChart = echarts.init(document.getElementById('salesTrend'));
        var categoryChart = echarts.init(document.getElementById('categoryChart'));
        function loadData() {
            var startDate = $('#startDate').val();
            var endDate = $('#endDate').val();
            // 加载销售趋势数据
            $.ajax({
                url: '/api/report/sales-trend',
                method: 'GET',
                data: { startDate: startDate, endDate: endDate },
                success: function(response) {
                    var data = response.data;
                    trendChart.setOption({
                        title: { text: '销售趋势' },
                        tooltip: { trigger: 'axis' },
                        xAxis: { type: 'category', data: data.dates },
                        yAxis: { type: 'value', name: '销售额(元)' },
                        series: [{
                            name: '销售额',
                            type: 'line',
                            smooth: true,
                            data: data.amounts,
                            areaStyle: { color: 'rgba(54, 162, 235, 0.2)' },
                            itemStyle: { color: '#3398DB' }
                        }]
                    });
                }
            });
            // 加载分类占比数据
            $.ajax({
                url: '/api/report/category',
                method: 'GET',
                data: { startDate: startDate, endDate: endDate },
                success: function(response) {
                    var data = response.data;
                    categoryChart.setOption({
                        title: { text: '商品分类销售占比' },
                        tooltip: { trigger: 'item', formatter: '{b}: £{c} ({d}%)' },
                        series: [{
                            name: '销售占比',
                            type: 'pie',
                            radius: '60%',
                            data: data.map(function(item) {
                                return {
                                    name: item.category,
                                    value: item.totalAmount
                                };
                            }),
                            emphasis: {
                                itemStyle: {
                                    shadowBlur: 10,
                                    shadowOffsetX: 0,
                                    shadowColor: 'rgba(0, 0, 0, 0.5)'
                                }
                            }
                        }]
                    });
                }
            });
        }
        function exportPdf() {
            var startDate = $('#startDate').val();
            var endDate = $('#endDate').val();
            window.location.href = '/api/report/export/pdf?startDate=' + startDate + '&endDate=' + endDate;
        }
        function exportExcel() {
            var startDate = $('#startDate').val();
            var endDate = $('#endDate').val();
            window.location.href = '/api/report/export/excel?startDate=' + startDate + '&endDate=' + endDate;
        }
        // 页面加载时自动查询
        $(document).ready(function() {
            loadData();
        });
    </script>
</body>
</html>

JasperReports模板配置

src/main/resources/reports/sales_report.jrxml 中定义报表模板:

<?xml version="1.0" encoding="UTF-8"?>
<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              name="Sales Report" pageWidth="595" pageHeight="842">
    <parameter name="startDate" class="java.lang.String"/>
    <parameter name="endDate" class="java.lang.String"/>
    <parameter name="totalAmount" class="java.math.BigDecimal"/>
        <band height="50">
            <staticText>
                <reportElement x="180" y="10" width="200" height="30"/>
                <textElement textAlignment="Center">
                    <font size="16" isBold="true"/>
                </textElement>
                <text><![CDATA[销售报表]]></text>
            </staticText>
        </band>
    </title>
    <columnHeader>
        <band height="20">
            <staticText>
                <reportElement x="0" y="0" width="100" height="20"/>
                <text><![CDATA[订单号]]></text>
            </staticText>
            <staticText>
                <reportElement x="100" y="0" width="100" height="20"/>
                <text><![CDATA[商品名称]]></text>
            </staticText>
            <!-- 其他列... -->
        </band>
    </columnHeader>
    <detail>
        <band height="20">
            <textField>
                <reportElement x="0" y="0" width="100" height="20"/>
                <textFieldExpression><![CDATA[$F{orderNo}]]></textFieldExpression>
            </textField>
            <textField>
                <reportElement x="100" y="0" width="100" height="20"/>
                <textFieldExpression><![CDATA[$F{productName}]]></textFieldExpression>
            </textField>
            <!-- 其他列... -->
        </band>
    </detail>
    <summary>
        <band height="30">
            <staticText>
                <reportElement x="0" y="0" width="100" height="20"/>
                <text><![CDATA[总金额:]]></text>
            </staticText>
            <textField>
                <reportElement x="100" y="0" width="100" height="20"/>
                <textFieldExpression><![CDATA[$P{totalAmount}]]></textFieldExpression>
            </textField>
        </band>
    </summary>
</jasperReport>

项目配置

pom.xml 关键依赖:

<dependencies>
    <!-- Spring Boot -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- MyBatis-Plus -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.3.1</version>
    </dependency>
    <!-- MySQL -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <!-- JasperReports -->
    <dependency>
        <groupId>net.sf.jasperreports</groupId>
        <artifactId>jasperreports</artifactId>
        <version>6.20.0</version>
    </dependency>
    <!-- EasyExcel -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>easyexcel</artifactId>
        <version>3.3.2</version>
    </dependency>
    <!-- Lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

核心功能演示效果

  1. 趋势图:折线图展示每日销售额变化
  2. 分类饼图:展示不同商品分类的销售占比
  3. PDF导出:服务端生成专业格式的PDF报表
  4. Excel导出:支持数据分析和二次加工

优化建议

优化方向 实现方案
大数据量 使用Elasticsearch做聚合分析
缓存 Redis缓存高频查询结果
异步 Spring @Async异步生成大报表
安全 加入Shiro/JWT权限控制

扩展功能

  • 定时报表:使用Spring Scheduled定时发送邮件
  • 多维度分析:按客户、区域、时间等多维度切片
  • 可视化大屏:接入Grafana或自研大屏
  • 智能预警:销售额异常自动告警

这个案例覆盖了报表系统从数据查询、可视化展示到文件导出的完整流程,您可以根据实际业务需求调整数据统计逻辑和展示样式。

抱歉,评论功能暂时关闭!