|
@@ -0,0 +1,351 @@
|
|
|
|
|
+package com.hotent.util;
|
|
|
|
|
+
|
|
|
|
|
+import java.math.BigDecimal;
|
|
|
|
|
+import java.text.DecimalFormat;
|
|
|
|
|
+import java.util.*;
|
|
|
|
|
+import java.util.stream.Collectors;
|
|
|
|
|
+/**
|
|
|
|
|
+ *@author: zhao yue yue
|
|
|
|
|
+ *@create: 2025-12-08 10:05
|
|
|
|
|
+ */
|
|
|
|
|
+public class CostDataUtil {
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 将查询结果转换为目标格式
|
|
|
|
|
+ * @param maps 查询结果:包含序号(xuhao)、项目名称(xiangmu)、年份(nianfen)、数值(shuzhi)和rowid
|
|
|
|
|
+ * @return 转换后的数据结构
|
|
|
|
|
+ */
|
|
|
|
|
+ public Map<String, Object> convertToCostFormat(List<Map<String, Object>> maps) {
|
|
|
|
|
+ if (maps == null || maps.isEmpty()) {
|
|
|
|
|
+ return createEmptyResult();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 1. 提取所有年份(从nianfen字段中提取年份数字)
|
|
|
|
|
+ Set<String> years = extractYearsFromData(maps);
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 按序号和项目名称分组
|
|
|
|
|
+ Map<String, Map<String, String>> groupedData = groupBySerialAndProject(maps, years);
|
|
|
|
|
+
|
|
|
|
|
+ // 3. 构建costItems列表(按序号排序)
|
|
|
|
|
+ List<Map<String, Object>> costItems = buildCostItems(groupedData, years);
|
|
|
|
|
+
|
|
|
|
|
+ // 4. 添加合计行(可选)
|
|
|
|
|
+ addTotalItem(costItems, groupedData, years);
|
|
|
|
|
+
|
|
|
|
|
+ // 5. 返回最终结果
|
|
|
|
|
+ return buildResult(years, costItems);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 从数据中提取所有年份
|
|
|
|
|
+ */
|
|
|
|
|
+ private Set<String> extractYearsFromData(List<Map<String, Object>> maps) {
|
|
|
|
|
+ return maps.stream()
|
|
|
|
|
+ .map(row -> {
|
|
|
|
|
+ String nianfen = (String) row.get("nianfen");
|
|
|
|
|
+ if (nianfen != null) {
|
|
|
|
|
+ // 提取年份数字,如从"2024年核定值"中提取"2024"
|
|
|
|
|
+ return extractYearNumber(nianfen);
|
|
|
|
|
+ }
|
|
|
|
|
+ return null;
|
|
|
|
|
+ })
|
|
|
|
|
+ .filter(Objects::nonNull)
|
|
|
|
|
+ .filter(year -> !year.isEmpty())
|
|
|
|
|
+ .collect(Collectors.toCollection(TreeSet::new)); // 自动排序
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 按序号和项目名称分组
|
|
|
|
|
+ */
|
|
|
|
|
+ private Map<String, Map<String, String>> groupBySerialAndProject(
|
|
|
|
|
+ List<Map<String, Object>> maps,
|
|
|
|
|
+ Set<String> years) {
|
|
|
|
|
+
|
|
|
|
|
+ // 使用LinkedHashMap保持插入顺序
|
|
|
|
|
+ Map<String, Map<String, String>> result = new LinkedHashMap<>();
|
|
|
|
|
+
|
|
|
|
|
+ for (Map<String, Object> row : maps) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ String xuhao = (String) row.get("xuhao");
|
|
|
|
|
+ String xiangmu = (String) row.get("xiangmu");
|
|
|
|
|
+ String nianfen = (String) row.get("nianfen");
|
|
|
|
|
+ Object shuzhiObj = row.get("shuzhi");
|
|
|
|
|
+
|
|
|
|
|
+ if (xuhao == null || xiangmu == null || nianfen == null) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 生成唯一键:序号 + "|" + 项目名称
|
|
|
|
|
+ String key = xuhao + "|" + xiangmu;
|
|
|
|
|
+
|
|
|
|
|
+ // 初始化项目数据
|
|
|
|
|
+ if (!result.containsKey(key)) {
|
|
|
|
|
+ result.put(key, new HashMap<>());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 提取年份
|
|
|
|
|
+ String year = extractYearNumber(nianfen);
|
|
|
|
|
+ if (year != null && years.contains(year)) {
|
|
|
|
|
+ // 转换值为字符串
|
|
|
|
|
+ String value = "";
|
|
|
|
|
+ if (shuzhiObj != null) {
|
|
|
|
|
+ String strValue = shuzhiObj.toString().trim();
|
|
|
|
|
+ if (!strValue.isEmpty()) {
|
|
|
|
|
+ value = strValue;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ result.get(key).put(year, value);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ // 跳过错误数据
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 构建costItems列表,按序号排序
|
|
|
|
|
+ */
|
|
|
|
|
+ private List<Map<String, Object>> buildCostItems(
|
|
|
|
|
+ Map<String, Map<String, String>> groupedData,
|
|
|
|
|
+ Set<String> years) {
|
|
|
|
|
+
|
|
|
|
|
+ List<Map<String, Object>> costItems = new ArrayList<>();
|
|
|
|
|
+
|
|
|
|
|
+ // 按序号排序(先按中文序号,再按数字序号)
|
|
|
|
|
+ List<String> sortedKeys = groupedData.keySet().stream()
|
|
|
|
|
+ .sorted((key1, key2) -> {
|
|
|
|
|
+ // 分割键:序号|项目名称
|
|
|
|
|
+ String[] parts1 = key1.split("\\|");
|
|
|
|
|
+ String[] parts2 = key2.split("\\|");
|
|
|
|
|
+ String xuhao1 = parts1[0];
|
|
|
|
|
+ String xuhao2 = parts2[0];
|
|
|
|
|
+
|
|
|
|
|
+ // 自定义排序:先中文序号,后数字序号
|
|
|
|
|
+ return compareChineseAndNumber(xuhao1, xuhao2);
|
|
|
|
|
+ })
|
|
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
|
+
|
|
|
|
|
+ for (String key : sortedKeys) {
|
|
|
|
|
+ String[] parts = key.split("\\|");
|
|
|
|
|
+ String xuhao = parts[0];
|
|
|
|
|
+ String xiangmu = parts[1];
|
|
|
|
|
+
|
|
|
|
|
+ Map<String, Object> item = new HashMap<>();
|
|
|
|
|
+
|
|
|
|
|
+ // 使用项目名称作为costItemName
|
|
|
|
|
+ item.put("costItemName", xiangmu);
|
|
|
|
|
+ // 如果需要,也可以添加序号字段
|
|
|
|
|
+ item.put("serialNumber", xuhao);
|
|
|
|
|
+
|
|
|
|
|
+ // 添加年份数据
|
|
|
|
|
+ Map<String, String> yearValues = new HashMap<>();
|
|
|
|
|
+ Map<String, String> projectYearData = groupedData.get(key);
|
|
|
|
|
+
|
|
|
|
|
+ for (String year : years) {
|
|
|
|
|
+ String value = projectYearData != null ?
|
|
|
|
|
+ projectYearData.getOrDefault(year, "") : "";
|
|
|
|
|
+ yearValues.put(year, value);
|
|
|
|
|
+ }
|
|
|
|
|
+ item.put("yearValues", yearValues);
|
|
|
|
|
+
|
|
|
|
|
+ costItems.add(item);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return costItems;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 添加合计行
|
|
|
|
|
+ */
|
|
|
|
|
+ private void addTotalItem(
|
|
|
|
|
+ List<Map<String, Object>> costItems,
|
|
|
|
|
+ Map<String, Map<String, String>> groupedData,
|
|
|
|
|
+ Set<String> years) {
|
|
|
|
|
+
|
|
|
|
|
+ Map<String, Object> totalItem = new HashMap<>();
|
|
|
|
|
+ totalItem.put("costItemName", "合计");
|
|
|
|
|
+
|
|
|
|
|
+ Map<String, String> totalValues = new HashMap<>();
|
|
|
|
|
+
|
|
|
|
|
+ for (String year : years) {
|
|
|
|
|
+ BigDecimal total = BigDecimal.ZERO;
|
|
|
|
|
+
|
|
|
|
|
+ for (Map<String, String> projectYearData : groupedData.values()) {
|
|
|
|
|
+ String valueStr = projectYearData.get(year);
|
|
|
|
|
+ if (valueStr != null && !valueStr.trim().isEmpty()) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 移除逗号等分隔符
|
|
|
|
|
+ String cleanValue = valueStr.replaceAll("[^\\d.-]", "");
|
|
|
|
|
+ if (!cleanValue.isEmpty()) {
|
|
|
|
|
+ BigDecimal value = new BigDecimal(cleanValue);
|
|
|
|
|
+ total = total.add(value);
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
|
|
+ // 忽略转换错误
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 格式化合计值(添加千分位)
|
|
|
|
|
+ String totalStr = total.compareTo(BigDecimal.ZERO) > 0 ?
|
|
|
|
|
+ formatNumberWithCommas(total) : "";
|
|
|
|
|
+ totalValues.put(year, totalStr);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ totalItem.put("yearValues", totalValues);
|
|
|
|
|
+ costItems.add(totalItem);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 构建最终结果
|
|
|
|
|
+ */
|
|
|
|
|
+ private Map<String, Object> buildResult(Set<String> years, List<Map<String, Object>> costItems) {
|
|
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
|
|
+ result.put("years", new ArrayList<>(years));
|
|
|
|
|
+ result.put("costItems", costItems);
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 提取年份数字
|
|
|
|
|
+ */
|
|
|
|
|
+ private String extractYearNumber(String nianfen) {
|
|
|
|
|
+ if (nianfen == null) return null;
|
|
|
|
|
+
|
|
|
|
|
+ // 匹配4位数字年份
|
|
|
|
|
+ java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("\\d{4}");
|
|
|
|
|
+ java.util.regex.Matcher matcher = pattern.matcher(nianfen);
|
|
|
|
|
+ if (matcher.find()) {
|
|
|
|
|
+ return matcher.group();
|
|
|
|
|
+ }
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 比较中文序号和数字序号
|
|
|
|
|
+ */
|
|
|
|
|
+ private int compareChineseAndNumber(String xuhao1, String xuhao2) {
|
|
|
|
|
+ // 中文序号映射
|
|
|
|
|
+ Map<String, Integer> chineseMap = new HashMap<>();
|
|
|
|
|
+ chineseMap.put("一", 1);
|
|
|
|
|
+ chineseMap.put("二", 2);
|
|
|
|
|
+ chineseMap.put("三", 3);
|
|
|
|
|
+ chineseMap.put("四", 4);
|
|
|
|
|
+ chineseMap.put("五", 5);
|
|
|
|
|
+ chineseMap.put("六", 6);
|
|
|
|
|
+ chineseMap.put("七", 7);
|
|
|
|
|
+ chineseMap.put("八", 8);
|
|
|
|
|
+ chineseMap.put("九", 9);
|
|
|
|
|
+ chineseMap.put("十", 10);
|
|
|
|
|
+
|
|
|
|
|
+ // 尝试将字符串转换为数字
|
|
|
|
|
+ Integer num1 = chineseMap.get(xuhao1);
|
|
|
|
|
+ Integer num2 = chineseMap.get(xuhao2);
|
|
|
|
|
+
|
|
|
|
|
+ if (num1 == null) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ num1 = Integer.parseInt(xuhao1);
|
|
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
|
|
+ num1 = Integer.MAX_VALUE; // 无法转换的排到最后
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (num2 == null) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ num2 = Integer.parseInt(xuhao2);
|
|
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
|
|
+ num2 = Integer.MAX_VALUE; // 无法转换的排到最后
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return Integer.compare(num1, num2);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 格式化数字(添加千分位)
|
|
|
|
|
+ */
|
|
|
|
|
+ private String formatNumberWithCommas(BigDecimal number) {
|
|
|
|
|
+ DecimalFormat formatter = new DecimalFormat("#,##0");
|
|
|
|
|
+ return formatter.format(number);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 创建空结果
|
|
|
|
|
+ */
|
|
|
|
|
+ private Map<String, Object> createEmptyResult() {
|
|
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
|
|
+ result.put("years", new ArrayList<String>());
|
|
|
|
|
+ result.put("costItems", new ArrayList<Map<String, Object>>());
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 使用示例
|
|
|
|
|
+ */
|
|
|
|
|
+ public static void main(String[] args) {
|
|
|
|
|
+ // 模拟你的查询数据
|
|
|
|
|
+ List<Map<String, Object>> maps = new ArrayList<>();
|
|
|
|
|
+
|
|
|
|
|
+ // 添加测试数据(根据你提供的数据)
|
|
|
|
|
+ addRow(maps, "一", "人数", "2024年核定值", "");
|
|
|
|
|
+ addRow(maps, "一", "人数", "2025年核定值", "");
|
|
|
|
|
+ addRow(maps, "1", "1班", "2024年核定值", "");
|
|
|
|
|
+ addRow(maps, "1", "1班", "2025年核定值", "");
|
|
|
|
|
+ addRow(maps, "2", "2班", "2024年核定值", "");
|
|
|
|
|
+ addRow(maps, "2", "2班", "2025年核定值", "");
|
|
|
|
|
+ addRow(maps, "二", "1班费用", "2024年核定值", "");
|
|
|
|
|
+ addRow(maps, "二", "1班费用", "2025年核定值", "");
|
|
|
|
|
+ addRow(maps, "1", "押金", "2024年核定值", "");
|
|
|
|
|
+ addRow(maps, "1", "押金", "2025年核定值", "");
|
|
|
|
|
+ addRow(maps, "2", "缴纳", "2024年核定值", "");
|
|
|
|
|
+ addRow(maps, "2", "缴纳", "2025年核定值", "");
|
|
|
|
|
+ addRow(maps, "三", "班级乘法", "2024年核定值", "556");
|
|
|
|
|
+ addRow(maps, "三", "班级乘法", "2025年核定值", "85");
|
|
|
|
|
+ addRow(maps, "四", "人均费用", "2024年核定值", "223");
|
|
|
|
|
+ addRow(maps, "四", "人均费用", "2025年核定值", "225");
|
|
|
|
|
+
|
|
|
|
|
+ CostDataUtil converter = new CostDataUtil();
|
|
|
|
|
+ Map<String, Object> result = converter.convertToCostFormat(maps);
|
|
|
|
|
+
|
|
|
|
|
+ // 输出结果
|
|
|
|
|
+ System.out.println("Years: " + result.get("years"));
|
|
|
|
|
+
|
|
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
|
|
+ List<Map<String, Object>> costItems = (List<Map<String, Object>>) result.get("costItems");
|
|
|
|
|
+
|
|
|
|
|
+ System.out.println("\nCost Items:");
|
|
|
|
|
+ for (Map<String, Object> item : costItems) {
|
|
|
|
|
+ String costItemName = (String) item.get("costItemName");
|
|
|
|
|
+ System.out.println("\n项目: " + costItemName);
|
|
|
|
|
+
|
|
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
|
|
+ Map<String, String> yearValues = (Map<String, String>) item.get("yearValues");
|
|
|
|
|
+
|
|
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
|
|
+ List<String> years = (List<String>) result.get("years");
|
|
|
|
|
+
|
|
|
|
|
+ for (String year : years) {
|
|
|
|
|
+ String value = yearValues.get(year);
|
|
|
|
|
+ System.out.println(" " + year + "年: " + (value != null && !value.isEmpty() ? value : ""));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static void addRow(List<Map<String, Object>> list,
|
|
|
|
|
+ String xuhao, String xiangmu,
|
|
|
|
|
+ String nianfen, String shuzhi) {
|
|
|
|
|
+ Map<String, Object> row = new HashMap<>();
|
|
|
|
|
+ row.put("xuhao", xuhao);
|
|
|
|
|
+ row.put("xiangmu", xiangmu);
|
|
|
|
|
+ row.put("nianfen", nianfen);
|
|
|
|
|
+ row.put("shuzhi", shuzhi);
|
|
|
|
|
+ list.add(row);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+}
|