package com.ldj.springboot.importbean.utils;

import com.alibaba.fastjson.JSON;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * User: ldj
 * Date: 2025/10/24
 * Time: 0:14
 * Description: 分组统计并检查是否超预算
 */
public class GroupStream {

    public static void main(String[] args) {
        // 模拟申请数据
        List<ApplyInfo> applyList = new ArrayList<>();
        applyList.add(new ApplyInfo("2025", "09", "09", "T1", new BigDecimal("100")));
        applyList.add(new ApplyInfo("2024", "09", "09", "T2", new BigDecimal("100")));
        applyList.add(new ApplyInfo("2025", "11", "11", "T1", new BigDecimal("200")));

        // 配置年度预算上限
        BigDecimal yearAmount = new BigDecimal("500"); 

        // 1. 按 (year, deptCode, materialGroup) 分组,统计申请金额
        Map<BudgetKey, BigDecimal> applyAmountMap = applyList.stream()
                .collect(Collectors.groupingBy(
                        row -> new BudgetKey(row.getYear(), row.getDeptCode(), row.getMaterialGroup()),
                        Collectors.reducing(BigDecimal.ZERO, ApplyInfo::getAmount, BigDecimal::add)
                ));

        // 2. 提取所有需要查询“已使用金额”的 key(拼接成字符串)
        List<String> keysToQuery = applyAmountMap.keySet().stream().map(BudgetKey::getKey).collect(Collectors.toList());

        // 3. 批量查询:只调一次接口/数据库,获取所有已使用金额
        Map<String, BigDecimal> usedAmountMap = batchQueryUsedAmount(keysToQuery);

        // 4. 对比每项是否超预算
        List<String> exceededKeys = new ArrayList<>();
        for (Map.Entry<BudgetKey, BigDecimal> entry : applyAmountMap.entrySet()) {
            String key = BudgetKey.getKey(entry.getKey());
            BigDecimal itemAmount = entry.getValue();           // 本次申请金额
            BigDecimal usedAmount = usedAmountMap.getOrDefault(key, BigDecimal.ZERO); // 已使用金额(默认0)
            BigDecimal total = itemAmount.add(usedAmount);      // 总使用金额
            if (total.compareTo(yearAmount) > 0) {
                exceededKeys.add(key);
            }
        }

        // 5. 如果有超预算的项,抛出异常
        if (!CollectionUtils.isEmpty(exceededKeys)) {
            throw new IllegalArgumentException(JSON.toJSONString(exceededKeys) + " 已经超出年度预算金额!");
        }

        System.out.println("提交成功!");
    }

    /**
     * 模拟:批量查询“已使用金额”
     * 在真实项目中,这里应调用 DAO 或 RPC 批量接口
     */
    public static Map<String, BigDecimal> batchQueryUsedAmount(List<String> keys) {
        Map<String, BigDecimal> mockData = new HashMap<>();
        mockData.put("2025_09_T1", new BigDecimal("200"));
        mockData.put("2024_09_T2", new BigDecimal("100"));
        mockData.put("2025_11_T1", new BigDecimal("50"));

        // 只返回请求的 keys 对应的数据(模拟数据库 IN 查询)
        return keys.stream().collect(Collectors.toMap(key -> key, key -> mockData.getOrDefault(key, BigDecimal.ZERO)));
    }
}

@Data
@AllArgsConstructor
class BudgetKey {
    private String year;
    private String deptCode;
    private String materialGroup;

    /**
     * 将 BudgetKey 转为字符串 key,格式:year_deptCode_materialGroup
     */
    public static String getKey(BudgetKey budgetKey) {
        return String.join("_", budgetKey.getYear(), budgetKey.getDeptCode(), budgetKey.getMaterialGroup());
    }

    // 重写 equals 和 hashCode(重要!用于 groupingBy 正确分组)
    // 懒重写 equals 和 hashCode 使用注解 @Data
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof BudgetKey)) return false;
        BudgetKey budgetKey = (BudgetKey) o;
        return Objects.equals(year, budgetKey.year) &&
                Objects.equals(deptCode, budgetKey.deptCode) &&
                Objects.equals(materialGroup, budgetKey.materialGroup);
    }

    @Override
    public int hashCode() {
        return Objects.hash(year, deptCode, materialGroup);
    }
}

@Data
@AllArgsConstructor
class ApplyInfo {
    private String year;
    private String month;
    private String deptCode;
    private String materialGroup;
    private BigDecimal amount;
}

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐