钉钉审批实例解析工具类

背景

最近公司接入钉钉审批来完成一些功能。比如在线签约合同、财务报销等等。在整个流程中,是由钉钉发起审批,然后将结果推送到我们的服务中,然后在解析钉钉的审批实例表单来获取用户填写的数据,参与业务处理。
因为钉钉审批模版的原因,每个不同的审批模版,其审批实例信息不一样,之前是为每个不同的审批模版,写一套解析方法,通用型很差。针对公司的业务需求,写了一个解析审批实体信息的工具类。

注意:该工具类只提取用户输入的信息,钉钉模版组件自带的扩展信息没有处理。因为在我们的业务中还没有使用到,后续会根据具体的需求进行优化完善。

实现

定义通用表单对象

通过对比业务中使用的各个审批模版,定义了一个通用对象来封装钉钉组件中的数据。

public class ApprovalTemplateTableRes {
    private String id;
    private String name;
    private String value;
    private String label;
    private String extValue;
    //表单中表格内嵌使用的不是extValue
    private String extendValue;
}

定义一个FormCells注解

@Retention(RetentionPolicy.RUNTIME)
public @interface FormCells {
    /**
     * 审批表单名称
     */
    String value() default "";
    /**
     *  适配解析扩展的值。默认解析value字段值
     */
    boolean analyzeValue() default true;
    /**
     * 因为钉钉审批模版中可以创建表格,在钉钉审批实例中表格数据是一个嵌套集合的形式存在,所以在这里需要声明一下字段的名称
     * 
     */
    String targetCollectionName() default "";

    /**
     *  表单中该值是否是数组 默认是true 该字段用于对象属性上
     * @return
     */
    boolean isArray() default true;

}

使用反射实现解析工具类


import com.alibaba.fastjson.JSONObject;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author wyz
 */
public class DingTalkAnalyzeUtils {
    private DingTalkAnalyzeUtils(){}

    /**
     *  解析
     * @param resList
     * @param cls 解析后转换的实体类class 对象
     * @return 返回解析后实体类
     * @param <T>
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
     public static  <T> T process(List<ApprovalTemplateTableRes> resList, Class<T> cls) throws IllegalAccessException, InstantiationException {
        // 反射创建对象
        T obj = cls.newInstance();
        //反射获取待带有@FormCells 注解的所有字段属性 并将 @FormCells 的值作为key  Field 作为value 转化为map
        Field[] fields = cls.getDeclaredFields();
        Map<String, Field> fieldMap = Arrays.stream(fields).filter(field -> field.isAnnotationPresent(FormCells.class
        )).collect(Collectors.toMap(field -> field.getAnnotation(FormCells.class).name(), Function.identity()));
        // 遍历报文
        for (ApprovalTemplateTableRes res : resList) {
            // 获取钉钉审批表单项的名字
            String name = res.getName()==null ? res.getLabel() : res.getName();
            // 根据名称获取Field
            Field field = fieldMap.get(name);
            if (field == null){
                continue;
            }
            field.setAccessible(true);
            // 获取注解信息
            FormCells annotation = field.getAnnotation(FormCells.class);
            String value = annotation.analyzeValue()?res.getValue():res.getExtValue();
            //如果为null 说明是表格中内嵌的,在取一下extendValue值
            value = value != null ? value : res.getExtendValue();
            //取了两次没取到值,跳过
            if (value == null) {
                continue;
            }
            // 判断字段类型
            if (field.getType().isAssignableFrom(String.class)) {
                // 直接将value 赋值
                field.set(obj,value);
                continue;
            }
            // 字段类型是否为集合
            if(isCollection(field)){
                List<?> target;
                if ("".equals(annotation.targetCollectionName())){
                    //直接转化
                    target = JSONObject.parseArray(value,getTargetClass(field));
                }else{
                    // 获取钉钉审批表单项的值 转化为List  并递归解析获取解析后的列表
                    List<JSONObject> list = JSONObject.parseArray(value ,JSONObject.class);
                    target = list.stream().map(jsonObject -> {
                        String s = jsonObject.getString(annotation.targetCollectionName());
                        List<ApprovalTemplateTableRes> approvalTemplateTableRes = JSONObject.parseArray(s, ApprovalTemplateTableRes.class);
                        try {
                            return process(approvalTemplateTableRes, getTargetClass(field));
                        } catch (IllegalAccessException e) {
                            throw new RuntimeException(e);
                        } catch (InstantiationException e) {
                            throw new RuntimeException(e);
                        }
                    }).collect(Collectors.toList());
                }

                // 反射赋值
                field.set(obj,target);
                continue;
            }
            //todo 目前还没有遇到需要转化为对象的数据结构 暂时这样处理
            if (annotation.isArray()) {
                JSONObject jsonObject = JSONObject.parseObject(res.getValue(),JSONObject.class);
                //如果是对象
                List<ApprovalTemplateTableRes> list = JSONObject.parseArray(jsonObject.toString(),ApprovalTemplateTableRes.class);
                // 递归解析
                Object process = process(list, getTargetClass(field));
                // 将解析后的对象 进行反射赋值
                field.set(obj,process);
            } else{
                Object object = JSONObject.parseObject(value,getTargetClass(field));
                // 将解析后的对象 进行反射赋值
                field.set(obj,object);
            }

        }
        return obj;

    }


    /**
     *  获取Field 数据类型的class对象,如果是集合则返回集合范型的class 对象
     *  比如:
     *      List<T> 则返回 T.class
     *      String 则返回 String.class
     * @param field
     * @return
     */
    public static Class<?> getTargetClass(Field field){
        Type type = field.getGenericType();
        if (type instanceof ParameterizedType){
            for (Type actualTypeArgument : ((ParameterizedType) type).getActualTypeArguments()) {
                if (actualTypeArgument instanceof Class){
                    return (Class) actualTypeArgument;
                }
            }
        }
        return field.getType();

    }

    /**
     *  判断Field 是否是集合
     * @param field
     * @return
     */
    public static boolean isCollection(Field field) {
        return   field.getType().isAssignableFrom(List.class)||
                field.getType().isAssignableFrom(Set.class)||
                field.getType().isAssignableFrom(Collection.class);

    }

}


验证

审批表单数据

[
    {
        "id": "DepartmentField_1CJEB0424S680",
        "name": "门店",
        "value": "黄xx区域",
        "bizAlias": "",
        "extValue": "[{\"number\":150,\"itemId\":\"859722845\",\"name\":\"黄xx区域\",\"id\":\"859722845\"}]",
        "componentType": "DepartmentField"
    },
    {
        "id": "DDSelectField_1DLJZ6ABOY1S0",
        "name": "出款卡",
        "value": "福州/厦门/盐城 营业额卡",
        "bizAlias": "",
        "extValue": "{\"label\":\"福州/厦门/盐城 营业额卡\",\"key\":\"option_B9K28MU0FPS0\"}",
        "componentType": "DDSelectField"
    },
    {
        "id": "DDSelectField_16YR31UGA22K0",
        "name": "费用是否需要多门店分摊",
        "value": "是",
        "bizAlias": "",
        "extValue": "{\"label\":\"是\",\"key\":\"option_0\"}",
        "componentType": "DDSelectField"
    },
    {
        "id": "DDMultiSelectField_1XGUVGJSRAF40",
        "name": "费用项目(可多选)",
        "value": "[\"奖金/绩效\"]",
        "bizAlias": "",
        "extValue": "[{\"label\":\"奖金/绩效\",\"key\":\"option_1Y9PKK1BLYTC0\"}]",
        "componentType": "DDMultiSelectField"
    },
    {
        "id": "DDMultiSelectField_1SIB5630HWBK0",
        "name": "费用发生周期",
        "value": "[\"7月\"]",
        "bizAlias": "",
        "extValue": "[{\"label\":\"7月\",\"key\":\"option_PIZFGAFJT740\"}]",
        "componentType": "DDMultiSelectField"
    },
    {
        "id": "InnerContactField_1ZYZ2D7EOEG00",
        "name": "寿星/被介绍人",
        "bizAlias": "",
        "componentType": "InnerContactField"
    },
    {
        "id": "TextField_1G2PWI104B0G0",
        "name": "业务描述",
        "value": "7月管理分红营运基金分摊",
        "bizAlias": "",
        "componentType": "TextField"
    },
    {
        "id": "TableField_1HUBAFEXIIYO0",
        "name": "多项目报销须添加明细",
        "value": "[{\"rowValue\":[{\"bizAlias\":\"\",\"label\":\"项目名称\",\"value\":\"7月绩效管理费用\",\"key\":\"TextField_N9L1IDXS4E80\",\"mask\":false},{\"bizAlias\":\"\",\"label\":\"数量\",\"value\":\"1\",\"key\":\"NumberField_10BMA0B2WM9C0\",\"mask\":false},{\"bizAlias\":\"\",\"label\":\"单价(元)\",\"value\":\"1723.8\",\"key\":\"MoneyField_IJ2P3XLTCM80\",\"mask\":false},{\"bizAlias\":\"\",\"label\":\"小计金额(元)\",\"value\":\"1723.8\",\"key\":\"CalculateField_YFQHZV3OIZK0\",\"mask\":false}],\"rowNumber\":\"TableField_1HUBAFEXIIYO0_1TC2MNOW37BB4\"}]",
        "bizAlias": "",
        "extValue": "{\"statValue\":[],\"componentName\":\"TableField\"}",
        "componentType": "TableField"
    },
    {
        "id": "MoneyField_1JUY3KWFUEYO0",
        "name": "合计总金额",
        "value": "1723.8",
        "bizAlias": "",
        "extValue": "{\"upper\":\"壹仟柒佰贰拾叁元捌角\",\"componentName\":\"MoneyField\"}",
        "componentType": "MoneyField"
    },
    {
        "id": "DDSelectField_1PSZW55OHU9S0",
        "name": "是否录入系统",
        "bizAlias": "",
        "componentType": "DDSelectField"
    },
    {
        "id": "RecipientAccountField-JVLZMITA",
        "name": "收款账号",
        "value": "黄xx",
        "bizAlias": "",
        "extValue": "{\"identityType\":\"PERSONAL_BANK_CARD\",\"instProvince\":\"福建省\",\"name\":\"黄xx\",\"instCity\":\"福州市\",\"instBranchName\":\"中国建设银行股份有限公司福州师大支行\",\"id\":\"202305271648xxxx\",\"instCode\":\"CCB\",\"instName\":\"中国建设银行\",\"cardNo\":\"6217001xxxxxx\"}",
        "componentType": "RecipientAccountField"
    },
    {
        "id": "TextField_1VIP4V7Z20HS",
        "name": "收款账号所属开户行",
        "value": "62170018200xxxxxx 中国建设银行福州师大支行",
        "componentType": "TextField"
    },
    {
        "id": "TextField_R353BQ7OR0W0",
        "name": "备注",
        "bizAlias": "",
        "componentType": "TextField"
    },
    {
        "id": "RelateField_RFFZEC7G3TC0",
        "name": "关联审批单",
        "bizAlias": "",
        "componentType": "RelateField"
    },
    {
        "id": "DDPhotoField_15OQX42UGALC0",
        "name": "报销面单&报销凭证",
        "value": "[\"https://static.dingtalk.com/media/lQLPM5ejsYExxxxxxx.png\"]",
        "bizAlias": "",
        "componentType": "DDPhotoField"
    },
    {
        "id": "DDAttachment_1G1TPEVEFVPC0",
        "name": "费用多门店摊销附件",
        "value": "[{\"spaceId\":\"4152693639\",\"fileName\":\"2023.7月损益月表汇总-黄先明区域(分红).xlsx\",\"fileSize\":\"72907\",\"fileType\":\"xlsx\",\"fileId\":\"112942110479\"}]",
        "bizAlias": "",
        "componentType": "DDAttachment"
    },
    {
        "id": "DDPhotoField_DIH7U9J4V1C0",
        "name": "后附单据",
        "bizAlias": "",
        "componentType": "DDPhotoField"
    },
    {
        "id": "DDAttachment_IW1IJGI4IQO0",
        "name": "文件附件",
        "bizAlias": "",
        "componentType": "DDAttachment"
    },
    {
        "id": "DDSelectField_1OW0M3G7RQOW0",
        "name": "11",
        "bizAlias": "",
        "componentType": "DDSelectField"
    },
    {
        "id": "TextNote_MR574BV2ECW0",
        "bizAlias": "",
        "componentType": "TextNote"
    },
    {
        "id": "TextNote_AUJZIXEWB8C0",
        "value": "员工福利审批流程:\n》申请人→区域人资→区域经理→单店财务→出纳 \n区域人资划分:\n苏州",
        "bizAlias": "",
        "componentType": "TextNote"
    }
]

定义对应的实体对象


import lombok.Data;

import java.util.List;

@Data
public class StoreProcessInstance {

    @FormCells(value = "门店")
    private String store;

    @FormCells(value = "出款卡")
    private String cardNo;

    @FormCells(value = "费用是否需要多门店分摊")
    private String isShare;

    @FormCells(value = "费用项目(可多选)")
    private String feeItem;

    //费用发生周期
    @FormCells(value =  "费用发生周期")
    private String feeCycle;

    //业务描述
    @FormCells(value = "业务描述")
    private String businessDesc;

    @FormCells(value = "多项目报销须添加明细", targetCollectionName = "rowValue")
    private List<Detail> detail;

    @FormCells(value = "收款账号")
    private String account;


}

@Data
public class Detail{

    @FormCells(value = "项目名称")
    private String projectName;
    @FormCells(value = "数量")
    private String num;
    @FormCells(value = "单价(元)")
    private String price;
    @FormCells(value =  "小计金额(元)")
    private String total;

}

使用

    @PostMapping("/store")
    public void test2(@RequestBody List<ApprovalTemplateTableRes> resList) throws IllegalAccessException, InstantiationException {
        StoreProcessInstance instance = DingTalkAnalyzeUtils.process(resList, StoreProcessInstance.class);
        log.info("instance:{}",instance);
    }

结果输出

instance:StoreProcessInstance(store=黄xx区域, cardNo=福州/厦门/盐城 营业额卡, isShare=, feeItem=["奖金/绩效"], feeCycle=["7月"], businessDesc=7月管理分红营运基金分摊, detail=[Detail(projectName=7月绩效管理费用, num=1, price=1723.8, total=1723.8)], account=黄xx)


说明

该工具类主要是通过反射类解析审批表单数据,提取用户输出的数据,该工具类,是根据公司业务使用的审批模版开发出来的,仅供参考。

  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值