41.仿简道云公式函数实战-数学函数-SUMIF

1. SUMIF函数

SUMIF 函数可用于计算子表单中满足某一条件的数字相加并返回和。

2. 函数用法

SUMIF(range, criteria, [sum_range])

其中各参数的含义及使用方法如下:

  • range:必需;根据 criteria 的条件规则进行检测的判断字段。支持的字段包括:子表单中的数字、单行文本、下拉框、单选按钮组;

  • criteria:必需;用于判断的条件规则。支持的形式和使用规则如下表:

支持形式是否需要加引号示例注意事项
数字不需要20、32
表达式需要“>32”、"!=苹果"支持的运算符号包括:>、<、==、!=、>=、<=
文本需要“苹果”、"水果"
字段不需要字段1)在主表字段中使用 SUMIF 函数时,只能选择主表字段2)在子表字段中使用 SUMIF 函数时,只能选择择主表字段和当前子表字段

3. 函数示例

如,计算入库明细中产品类型为「水果」的全部入库数量,则可以在「水果类数量总计」字段设置公式为:

4. 代码实战

首先我们在function包下创建math包,在math包下创建SumIfFunction类,代码如下:

package com.ql.util.express.self.combat.function.math;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.ql.util.express.Operator;
import com.ql.util.express.self.combat.exception.FormulaException;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * 类描述: SUMIF函数
 *
 * @author admin
 * @version 1.0.0
 * @date 2023/11/24 10:33
 */
public class SumIfFunction extends Operator {

    public SumIfFunction(String name) {
        this.name = name;
    }

    @Override
    public Object executeInner(Object[] lists) throws Exception {

        //边界判断
        if (lists.length == 0 || lists.length<3 || lists.length >4) {
            throw new FormulaException("操作数异常");
        }

        BigDecimal res = BigDecimal.ZERO;
        Object range = null;
        Object criteria = null;
        List<Map<String,Object>> subFormVal =null;
        String rangeS ="";
        String key = "";
        if (lists.length == 3) { // 两个参数
            // 获取参数
            key = lists[0].toString();
            range = lists[1];
            criteria = lists[2];

            rangeS = range.toString();
            subFormVal = JSON.parseObject(key,List.class);
            res = cal(subFormVal,rangeS,criteria.toString(),rangeS);
        } else {
            // 三个参数处理
            Object sumRange = lists[3];
            key = lists[0].toString();
            range = lists[1];
            criteria = lists[2];

            rangeS = range.toString();
            subFormVal = JSON.parseObject(key,List.class);
            // 循环subFormVal集合
            res = cal(subFormVal,rangeS,criteria.toString(),sumRange.toString());
        }
        return res;
    }

    // 计算结果
    private BigDecimal cal(List<Map<String, Object>> list, String range, String criteria, String sumRange) {

        // criteria判断类型
        boolean isNum = CriteriaUtil.isNum(criteria);
        boolean isExpress = CriteriaUtil.isExpress(criteria);

        // 根据criteria类型 生成Predicate
        List<Map<String, Object>> collect =null;
        if (isExpress) {// 如果是表达式
            // 提取符号
            String symbol = CriteriaUtil.extractSymbol(criteria);
            String symbol_value = criteria.replaceAll(symbol,"");
            boolean sybolValueIsNum = CriteriaUtil.isNum(symbol_value);
            if (sybolValueIsNum) {// 如果是数字
                collect = list.stream().filter(paramMap -> {
                    boolean res = false;
                    if ("==".equals(symbol)) {
                        res = Double.parseDouble(paramMap.get(range).toString()) == Double.parseDouble(symbol_value)?true:false;
                    } else if (">".equals(symbol)) {
                        res = Double.parseDouble(paramMap.get(range).toString()) > Double.parseDouble(symbol_value)?true:false;
                    } else if (">=".equals(symbol)) {
                        res = Double.parseDouble(paramMap.get(range).toString()) >= Double.parseDouble(symbol_value)?true:false;
                    } else if ("<".equals(symbol)) {
                        res = Double.parseDouble(paramMap.get(range).toString()) < Double.parseDouble(symbol_value)?true:false;
                    } else if ("<=".equals(symbol)) {
                        res = Double.parseDouble(paramMap.get(range).toString()) <= Double.parseDouble(symbol_value)?true:false;
                    } else if ("!=".equals(symbol)) {
                        res = Double.parseDouble(paramMap.get(range).toString()) != Double.parseDouble(symbol_value)?true:false;
                    }
                    return res;
                }).collect(Collectors.toList());
            } else {
                collect = list.stream().filter(paramMap -> {
                    boolean res = false;
                    if ("==".equals(symbol)) {
                        res = String.valueOf(paramMap.get(range)).equals(symbol_value);
                    } else if ("!=".equals(symbol)) {
                        res = !(String.valueOf(paramMap.get(range)).equals(symbol_value));
                    } else {
                        throw new RuntimeException("字符暂不支持的操作符号为:"+symbol);
                    }
                    return res;
                }).collect(Collectors.toList());
            }

        } else {// 没有表达式 直接默认为==
            if (isNum) {// 如果是数字
                collect = list.stream().filter(paramMap -> Double.parseDouble(paramMap.get(range).toString()) == Double.parseDouble(criteria)?true:false).collect(Collectors.toList());
            } else {
                collect = list.stream().filter(paramMap -> String.valueOf(paramMap.get(range)).equals(criteria)).collect(Collectors.toList());
            }
        }

        // 满足条件的集合统计出来后,按照sumRange字段统计求和
        BigDecimal sum = BigDecimal.ZERO;
        for (Map<String,Object> map:collect) {
            BigDecimal tmp = new BigDecimal(map.get(sumRange).toString());
            sum = sum.add(tmp);
        }
        return sum;

    }


    static class CriteriaUtil {

        public static boolean isNum (String criteria) {
            return StrUtil.isNumeric(criteria);
        }
        public static boolean isExpress(String criteria) {
            List<String> symbols = Arrays.asList(">",">=","<","<=","==","!=");
            boolean res = symbols.stream().anyMatch(s -> criteria.contains(s));
            return res;
        }

        /***
         * 提取表达式中的符号
         * @param criteria
         * @return
         */
        public static String extractSymbol(String criteria) {
            List<String> symbols = Arrays.asList(">",">=","<","<=","==","!=");
            final Optional<String> first = symbols.stream().filter(new Predicate<String>() {
                @Override
                public boolean test(String s) {
                    return criteria.contains(s);
                }
            }).findFirst();
            return first.get();
        }
    }

}

把SumIfFunction类注册到公式函数入口类中,代码如下:

package com.ql.util.express.self.combat.ext;

import com.ql.util.express.ExpressRunner;
import com.ql.util.express.IExpressResourceLoader;
import com.ql.util.express.parse.NodeTypeManager;
import com.ql.util.express.self.combat.function.logic.*;
import com.ql.util.express.self.combat.function.math.*;

/**
 * 类描述: 仿简道云公式函数实战入口类
 *
 * @author admin
 * @version 1.0.0
 * @date 2023/11/21 15:29
 */
public class FormulaRunner extends ExpressRunner {

    public FormulaRunner() {
        super();
    }

    public FormulaRunner(boolean isPrecise, boolean isTrace) {
        super(isPrecise,isTrace);
    }

    public FormulaRunner(boolean isPrecise, boolean isStrace, NodeTypeManager nodeTypeManager) {
        super(isPrecise,isStrace,nodeTypeManager);
    }

    public FormulaRunner(boolean isPrecise, boolean isTrace, IExpressResourceLoader iExpressResourceLoader, NodeTypeManager nodeTypeManager) {
        super(isPrecise,isTrace,iExpressResourceLoader,nodeTypeManager);
    }

    @Override
    public void addSystemFunctions() {
        // ExpressRunner 的内部系统函数
        super.addSystemFunctions();
        // 扩展公式函数
        this.customFunction();
    }
    /***
     * 自定义公式函数
     */
    public void customFunction() {

        // 逻辑公式函数
        this.addLogicFunction();

        // 数学公式函数
        this.addMathFunction();
    }

    public void addLogicFunction() {
        // AND函数
        this.addFunction("AND",new AndFunction("AND"));

        // IF函数
        this.addFunction("IF",new IfFunction("IF"));

        // IFS函数
        this.addFunction("IFS",new IfsFunction("IFS"));

        // XOR函数
        this.addFunction("XOR",new XorFunction("XOR"));

        // TRUE函数
        this.addFunction("TRUE",new TrueFunction("TRUE"));

        // FALSE函数
        this.addFunction("FALSE",new FalseFunction("FALSE"));

        // NOT函数
        this.addFunction("NOT",new NotFunction("NOT"));

        // OR函数
        this.addFunction("OR",new OrFunction("OR"));
    }

    public void addMathFunction() {
        // ABS函数
        this.addFunction("ABS",new AbsFunction("ABS"));

        // AVERAGE函数
        this.addFunction("AVERAGE",new AvgFunction("AVERAGE"));

        // CEILING函数
        this.addFunction("CEILING",new CeilingFunction("CEILING"));

        // RADIANS函数
        this.addFunction("RADIANS",new RadiansFunction("RADIANS"));

        // COS函数
        this.addFunction("COS",new CosFunction("COS"));

        // COT函数
        this.addFunction("COT",new CotFunction("COT"));

        // COUNT函数
        this.addFunction("COUNT",new CountFunction("COUNT"));

        // COUNTIF函数
        this.addFunction("COUNTIF",new CountIfFunction("COUNTIF"));

        // FIXED函数
        this.addFunction("FIXED",new FixedFunction("FIXED"));

        // FLOOR函数
        this.addFunction("FLOOR",new FloorFunction("FLOOR"));

        // INT函数
        this.addFunction("INT",new IntFunction("INT"));

        // LARGE函数
        this.addFunction("LARGE",new LargeFunction("LARGE"));

        // LOG函数
        this.addFunction("LOG",new LogFunction("LOG"));

        // MAX函数
        this.addFunction("MAX",new MaxFunction("MAX"));

        // MIN函数
        this.addFunction("MIN",new MinFunction("MIN"));

        // MOD函数
        this.addFunction("MOD",new ModFunction("MOD"));

        // POWER函数
        this.addFunction("POWER",new PowerFunction("POWER"));

        // PRODUCT函数
        this.addFunction("PRODUCT",new ProductFunction("PRODUCT"));

        // RAND函数
        this.addFunction("RAND",new RandFunction("RAND"));

        // ROUND函数
        this.addFunction("ROUND",new RoundFunction("ROUND"));

        // SIN函数
        this.addFunction("SIN",new SinFunction("SIN"));

        // SMALL函数
        this.addFunction("SMALL",new SmallFunction("SMALL"));

        // SQRT函数
        this.addFunction("SQRT",new SqrtFunction("SQRT"));

        // SUM函数
        this.addFunction("SUM",new SumFunction("SUM"));

        // SUMIF函数
        this.addFunction("SUMIF",new SumIfFunction("SUMIF"));

    }
}

创建测试用例

package com.ql.util.express.self.combat;

import com.alibaba.fastjson.JSON;
import com.ql.util.express.DefaultContext;
import com.ql.util.express.self.combat.ext.FormulaRunner;
import org.junit.Test;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 类描述: 实战测试类
 *
 * @author admin
 * @version 1.0.0
 * @date 2023/11/21 15:45
 */
public class CombatTest {

    @Test
    public void SUMIF() throws Exception{

        FormulaRunner formulaRunner = new FormulaRunner(true,true);
        // 创建上下文
        DefaultContext<String, Object> context = new DefaultContext<>();
        List<Map<String,Object>> list = new ArrayList<>();
        Map<String,Object> map = new HashMap<>();
        map.put("record.type","红富士");
        map.put("record.name","苹果");
        map.put("record.num",20.0);

        Map<String,Object> map2 = new HashMap<>();
        map2.put("record.type","红富士");
        map2.put("record.name","苹果");
        map2.put("record.num",42.0);

        Map<String,Object> map3 = new HashMap<>();
        map3.put("record.type","红星");
        map3.put("record.name","苹果");
        map3.put("record.num",30.0);

        Map<String,Object> map4 = new HashMap<>();
        map4.put("record.type","美国");
        map4.put("record.name","苹果");
        map4.put("record.num",13000.0);

        Map<String,Object> map5 = new HashMap<>();
        map5.put("record.type","夏黑");
        map5.put("record.name","葡萄");
        map5.put("record.num",15);

        Map<String,Object> map6 = new HashMap<>();
        map6.put("record.type","阳光玫瑰");
        map6.put("record.name","葡萄");
        map6.put("record.num",30);

        Map<String,Object> map7 = new HashMap<>();
        map7.put("record.type","芝麻蕉");
        map7.put("record.name","香蕉");
        map7.put("record.num","20");

        list.add(map);
        list.add(map2);
        list.add(map3);

        list.add(map4);
        list.add(map5);
        list.add(map6);
        list.add(map7);
        String s = JSON.toJSONString(list);
        String express = "SUMIF(aa,bb,cc,dd)";
        context.put("aa",s);
        context.put("bb","record.type");
        context.put("cc","!=美国");
        context.put("dd","record.num");
        Object object = formulaRunner.execute(express, context, null, true, true);
        System.out.println(object);
    }

}

运行结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值