动态 SQL(行+列) + 动态表头(前端+EasyPoi) = 完整透视报表系统

下面给你一套完整可落地的"动态透视报表方案(查询 + 导出)",专门针对:

👉 SpringBoot + MyBatis + GaussDB + EasyPoi

👉 动态行分组(产品/型号/项目)+ 动态表头 + 动态汇总 + 页面接口 + 导出接口

重点我会强化:"动态表头 + 查询接口返回结构",让你前端也能直接渲染。


一、最终效果(你要实现的)

产品 型号 项目 6月 7月 8月 合计
AA AA AA 20 40 60 120
BB BB BB 40 60 80 180
合计 60 100 140 300

二、接口设计(核心)

✅ 查询接口(页面用)

http 复制代码
POST /pivot/query

返回:

json 复制代码
{
  "headers": [...动态表头...],
  "data": [...数据...]
}

✅ 导出接口(Excel)

http 复制代码
POST /pivot/export

三、Mapper 层(动态 SQL)

1️⃣ 接口

java 复制代码
public interface PivotMapper {

    List<String> queryMonths();

    List<Map<String, Object>> pivotData(
        @Param("months") List<String> months,
        @Param("groups") List<String> groups
    );
}

2️⃣ 查询动态列

xml 复制代码
<select id="queryMonths" resultType="string">
    SELECT DISTINCT month
    FROM sales_data
    ORDER BY month
</select>

⭐ 3️⃣ 核心透视 SQL

xml 复制代码
<select id="pivotData" resultType="map">

    SELECT

    <foreach collection="groups" item="g" separator=",">
        ${g}
    </foreach>

    <foreach collection="months" item="m">
        , SUM(amount) FILTER (WHERE month = #{m}) AS "${m}"
    </foreach>

    , SUM(amount) AS "合计"

    FROM sales_data

    GROUP BY
    <foreach collection="groups" item="g" separator=",">
        ${g}
    </foreach>

    UNION ALL

    SELECT

    <foreach collection="groups" item="g" separator=",">
        <choose>
            <when test="g == 'item'"> '合计' </when>
            <otherwise> '' </otherwise>
        </choose>
    </foreach>

    <foreach collection="months" item="m">
        , SUM(amount) FILTER (WHERE month = #{m})
    </foreach>

    , SUM(amount)

    FROM sales_data

</select>

四、Service(重点:动态表头构造)

java 复制代码
@Service
public class PivotService {

    @Autowired
    private PivotMapper pivotMapper;

    public Map<String, Object> query(List<String> groups) {

        checkGroups(groups);

        // 1. 动态列
        List<String> months = pivotMapper.queryMonths();

        // 2. 查询数据
        List<Map<String, Object>> data =
                pivotMapper.pivotData(months, groups);

        // 3. 构造动态表头(关键)
        List<Map<String, String>> headers =
                buildHeaders(groups, months);

        Map<String, Object> result = new HashMap<>();
        result.put("headers", headers);
        result.put("data", data);

        return result;
    }

    // ⭐ 动态表头(前端用)
    private List<Map<String, String>> buildHeaders(
            List<String> groups, List<String> months) {

        List<Map<String, String>> list = new ArrayList<>();

        // 行维度
        for (String g : groups) {
            list.add(header(getName(g), g));
        }

        // 动态列
        for (String m : months) {
            list.add(header(m, m));
        }

        // 合计
        list.add(header("合计", "合计"));

        return list;
    }

    private Map<String, String> header(String title, String field) {
        Map<String, String> map = new HashMap<>();
        map.put("title", title);
        map.put("field", field);
        return map;
    }

    private String getName(String field) {
        switch (field) {
            case "product": return "产品";
            case "model": return "型号";
            case "item": return "项目";
            default: return field;
        }
    }

    // 导出
    public Workbook export(List<String> groups) {

        Map<String, Object> result = query(groups);

        List<Map<String, Object>> data =
                (List<Map<String, Object>>) result.get("data");

        List<Map<String, String>> headers =
                (List<Map<String, String>>) result.get("headers");

        List<ExcelExportEntity> excelHeaders =
                buildExcelHeader(headers);

        ExportParams params =
                new ExportParams("动态报表", "sheet1");

        return ExcelExportUtil.exportExcel(params, excelHeaders, data);
    }

    // EasyPoi 表头
    private List<ExcelExportEntity> buildExcelHeader(
            List<Map<String, String>> headers) {

        List<ExcelExportEntity> list = new ArrayList<>();

        for (Map<String, String> h : headers) {
            list.add(new ExcelExportEntity(
                    h.get("title"), h.get("field")));
        }

        return list;
    }

    // 安全校验
    private void checkGroups(List<String> groups) {
        List<String> allow =
                Arrays.asList("product","model","item");

        for (String g : groups) {
            if (!allow.contains(g)) {
                throw new RuntimeException("非法字段:" + g);
            }
        }
    }
}

五、Controller(查询 + 导出)

java 复制代码
@RestController
@RequestMapping("/pivot")
public class PivotController {

    @Autowired
    private PivotService pivotService;

    // 查询接口(前端表格)
    @PostMapping("/query")
    public Map<String, Object> query(
            @RequestBody Map<String, Object> param) {

        List<String> groups =
                (List<String>) param.get("groups");

        return pivotService.query(groups);
    }

    // 导出接口
    @PostMapping("/export")
    public void export(
            @RequestBody Map<String, Object> param,
            HttpServletResponse response) throws Exception {

        List<String> groups =
                (List<String>) param.get("groups");

        Workbook wb = pivotService.export(groups);

        response.setContentType("application/vnd.ms-excel");
        response.setHeader("Content-Disposition",
                "attachment;filename=pivot.xls");

        wb.write(response.getOutputStream());
    }
}

六、前端渲染(重点)

后端返回:

json 复制代码
{
  "headers": [
    {"title":"产品","field":"product"},
    {"title":"型号","field":"model"},
    {"title":"项目","field":"item"},
    {"title":"6月","field":"6月"},
    {"title":"7月","field":"7月"},
    {"title":"8月","field":"8月"},
    {"title":"合计","field":"合计"}
  ],
  "data":[...]
}

👉 前端可以直接:

javascript 复制代码
columns = headers.map(h => ({
  title: h.title,
  dataIndex: h.field
}))

七、关键点总结(精华)

✅ 动态三核心

类型 实现
行分组 ${g}
FILTER
表头 headers + ExcelExportEntity

✅ 查询接口 = 导出接口基础

text 复制代码
query() → export()

⚠️ 安全(必须)

java 复制代码
白名单 groups

八、可升级(强烈建议)

🚀 1. 多级表头(年 → 月)

🚀 2. 多指标(数量 + 金额)

🚀 3. 前端拖拽维度(真正数据透视)

🚀 4. 大数据导出(分批写 Excel)


九、一句话总结

👉 动态 SQL(行+列) + 动态表头(前端+EasyPoi) = 完整透视报表系统


如果你下一步想做👇我可以帮你直接升级:

👉 多级表头(合并单元格)

👉 类 Excel 数据透视(拖拽维度)

👉 千万级数据不卡导出方案

直接说你的目标 👍

相关推荐
喝拿铁写前端20 分钟前
一套面向 Web、H5、小程序与 Flutter 的多端一致性技术方案
前端·架构
yaaakaaang26 分钟前
(一)前端,如此简单!---下载Nginx
前端·nginx
牛奶31 分钟前
为什么全国人民都能秒开同一个视频?
前端·http·cdn
KongHen021 小时前
uniapp-x实现自定义tabbar
前端·javascript·uni-app·unix
汪子熙1 小时前
TS2320 错误的本质、触发场景与在 Angular / RxJS 项目中的系统化应对
前端·javascript·angular.js
lzhdim1 小时前
SQL 入门 7:SQL 聚合与分组:函数、GROUP BY 与 ROLLUP
java·服务器·数据库·sql·mysql
lifewange1 小时前
INSERT INTO ... SELECT ...
数据库·sql
我命由我123451 小时前
React - BrowserRouter 与 HashRouter、push 模式与 replace 模式、编程式导航、withRouter
开发语言·前端·javascript·react.js·前端框架·html·ecmascript
Younglina1 小时前
用AI全自动生成连环画?我试了,效果惊艳!
前端·ai编程·claude
Devin_chen1 小时前
ES6 Class 渐进式详解
前端·javascript