Baoshi Document User Manual, and uses abstraction and reflection to create universal abstract classes to save development costs.

Document example

1024 is the best time to write a blog. Don’t say it, don’t say it, I suggest it become a legal holiday.

The official website of Baoshi documents: Baoshi official website
(@宝思TeamRemember to pay me for advertising)

Steps to use the Boucherie certificate
Use abstract classes to abstract the steps of obtaining fields, greatly reducing the amount of code. Save development costs.
The abstract class is at the bottom of the article, you can get it yourself if you need it.

Steps

1. Create reports
2. Configuration parameters
3. Create a data source object (javaBean)
4. Create a DataSet implementation class
5. Add the path to the DataSet implementation class in the com.basksoft.report.core.model.dataset.BeanDataset configuration file
6. Load custom bean data set
7. Add database report records

Create a report

Right click to create a report file

Configuration parameters

The parameters here are used to obtain data.

Create data source object

You need to define a empty parameter constructor and a constructor with a Boolean parameter, and assign default data to the attribute in this constructor.
The type of the Boolean constructor needs to be a boxed type (the boxed type of boolean is Boolean)
The purpose of adding the Boolean constructor is to obtain the object of default data.

@Data
public class AccountStatementVo {<!-- -->

    //Settlement unit
    private String clientName;

    //Billing date ends
    private Date reconcDeadline;

    // Converted currency amount currency system
    private String cyCode;

    //Amount converted to currency
    private BigDecimal amt;

    //Reconciliation method
    private String reconcMethod;

    // illustrate
    private String actstmDesc;

    // Bank Account
    private String bankacctName;

    // statement number
    private String actstmNo;

    public AccountStatementVo() {<!-- -->}

    public AccountStatementVo(Boolean defaultValue) {<!-- -->
        this.clientName = "Customer Name";
        this.reconcDeadline = new Date();
        this.cyCode = "CNY";
        this.amt = BigDecimal.ZERO;
        this.reconcMethod = "Test method";
        this.actstmDesc = "File Notes";
        this.bankacctName = "Bank of China";
        this.actstmNo = "HJ23424";
    }
}

Create DataSet implementation class

Add a DateSet class and inherit the AbstractBeanDataSet abstract class
The abstract class needs to specify two generic types. If the return result is a List type, the first generic type is specified as List, and the second type is specified as T. Otherwise, specify T

@Slf4j
public class BillDataset extends AbstractBeanDataSet<BillDocVo,BillDocVo> {<!-- -->

    @Override
    protected boolean getReturnDefaultData(BeanContext beanContext) {<!-- -->
        return false;
    }

    @Override
    public Result<BillDocVo> getDocVo(BeanContext beanContext) {<!-- -->
       return null;
    }

    @Override
    protected String name() {<!-- -->
        return "";
    }
}

Interface that returns List

public class DemoDataSet extends AbstractBeanDataSet<List<BillDetailVo>,BillDataset> {<!-- -->
    @Override
    protected boolean getReturnDefaultData(BeanContext beanContext) {<!-- -->
        return false;
    }

    @Override
    public Result<List<BillDetailVo>> getDocVo(BeanContext beanContext) {<!-- -->
        return null;
    }

    @Override
    protected String name() {<!-- -->
        return null;
    }
}

Override three methods in AbstractBeanDataSet

boolean getReturnDefaultData(BeanContext beanContext)

Obtain the parameters defined in the second step through beanContext.getReportParameter("billId")

Returns a boolean value, whether to obtain default data.

In this method, write your own conditions for obtaining default data.

 @Override
    protected boolean getReturnDefaultData(BeanContext beanContext) {<!-- -->
        Long billId = ParameterUtils.parseLong(beanContext.getReportParameter("billId"));

        //If the parameter is empty or 0, get the default data
        if (Objects.isNull(billId) || Objects.equals(billId, 0L)) {<!-- -->
            return true;
        }
        return false;
    }
Result getDocVo(BeanContext beanContext)

Method to obtain the data source.

In this method, the interface in the fms project is called through http to obtain the data source. (If necessary later, you can configure the fms data source in the report project and obtain data directly from the report project)

The returned data is the data source object created in the third step

Finally, write this interface to obtain the data source in fms and return the required data object

 @Override
    public Result<BillDocVo> getDocVo(BeanContext beanContext) {<!-- -->
        Long billId = ParameterUtils.parseLong(beanContext.getReportParameter("billId"));
        String token = (String) beanContext.getReportParameter("token");

        //Configure the access address in the FmsUrlConstants constant class
        String url = FmsUrlConstants.api + FmsUrlConstants.BILL_DOC;
        if (!StringUtils.hasText(url)) {<!-- -->
            return null;
        }

        // Use the hutool toolkit to send a get request. If the fms write is a post request, then send the post request.
        HttpResponse httpResponse = HttpRequest.get(url)
                .form("billId", billId)
                .header("token", token)
                .execute();
        
        //Convert the return result type
        Result<BillDocVo> result = JSONUtil.toBean(httpResponse.body(), new TypeReference<Result<BillDocVo>>() {<!-- --> }, false);

        return result;
    }
String name()

Returns the name of the data source object. The name displayed in the report is defined in this method.

 @Override
    protected String name() {<!-- -->
        return "Bill";
    }

Add dataSet class configuration

Load custom bean data set


Multiple data sets can be added

Add database report records

uni_acv_doc (document master table)

uni_acv_docmodel (single proof template table)

one-to-many relationship

Add report data to these two tables


Add the main document table (uni_acv_doc) and set fields according to respective needs.
doccode,tab_code need to be unique

When adding records to the document details template table (uni_acv_docmodel).

Set docid to the id of the main document table (uni_acv_doc),

Set file_id and sys_file_id to the fileId of the template,

Set is_default to 1

Other data is set as required

AbstractBeanDataSet abstract class

The functions of this abstract class:
If you want to obtain default data, call the Boolean constructor to create an object with default data.
Otherwise get the data obtained from fms.
Encapsulate all fields of the data object into a List<Map<String,Object>> object.
package com.yunwuyun.easy.baskreport.dataset;

import com.basksoft.report.core.model.dataset.BeanDataset;
import com.basksoft.report.core.model.dataset.impl.BeanContext;
import com.basksoft.report.core.model.dataset.impl.Field;
import com.basksoft.report.core.model.dataset.impl.FieldType;
import com.yunwuyun.easy.baskreport.common.Result;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;

/**
 * Document abstract class
 * @param <T>
 */
@Slf4j
public abstract class AbstractBeanDataSet<T,E> extends BeanDataset {<!-- -->

    protected Class<E> clazz;

    public AbstractBeanDataSet() {<!-- -->
        Type genericSuperclass = getClass().getGenericSuperclass();

        if (genericSuperclass instanceof ParameterizedType) {<!-- -->
            ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
            Type[] typeArguments = parameterizedType.getActualTypeArguments();

            if (typeArguments.length > 0) {<!-- -->
                Type typeArgument = typeArguments[1];

                if (typeArgument instanceof Class) {<!-- -->
                    this.clazz = (Class<E>) typeArgument;
                }
            }
        }
    }

    @SneakyThrows
    @Override
    public List<?> getData(BeanContext beanContext) {<!-- -->

        boolean returnDefaultData = false;
        returnDefaultData = this.getReturnDefaultData(beanContext);
        if(returnDefaultData) {<!-- -->
            return getDefaultData();
        }

        Result<T> result = this.getDocVo(beanContext);
        if (result == null || result.getCode() != 200) {<!-- -->
            return Collections.emptyList();
        }

        T t = result.getResult();

        List<Map<String,Object>> datas = getDatas(t);

        return datas;
    }

    protected abstract boolean getReturnDefaultData(BeanContext beanContext);

    private List<Map<String, Object>> getDatas(T t) {<!-- -->
        List<Map<String,Object>> datas = new ArrayList<>();

        if(isListType()) {<!-- -->
            List<E> list = (List<E>) t;
            for (E obj: list) {<!-- -->
                Map<String, Object> dataMap = getDataMap(obj);
                datas.add(dataMap);
            }
        } else {<!-- -->
            E obj = (E) t;
            Map<String, Object> data = getDataMap(obj);
            datas.add(data);
        }

        return datas;
    }


    /**
     * Get the map of all field attributes
     * @param t
     * @return
     */
    private Map<String, Object> getDataMap(E t) {<!-- -->

        List<Field> fields = this.getFields();
        Map<String, Object> data = new HashMap<>();

        for (Field field : fields) {<!-- -->
            String fieldName = field.getName();

            try {<!-- -->
                java.lang.reflect.Field fieldVo = this.clazz.getDeclaredField(fieldName);
                fieldVo.setAccessible(true);

                data.put(field.getName(), fieldVo.get(t));
            } catch (Exception e) {<!-- -->
                // Ignore the exception and continue processing the next field
                log.error("Dataset error,fieldName:{}, message:{}", fieldName, e.getMessage());
            }
        }

        return data;
    }

    /**
     * Whether the generic type is a list
     * @return
     */
    private Boolean isListType() {<!-- -->
        Type genericSuperclass = getClass().getGenericSuperclass();

        if (genericSuperclass instanceof ParameterizedType) {<!-- -->

            ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;

            Type[] typeArguments = parameterizedType.getActualTypeArguments();

            if (typeArguments.length > 0) {<!-- -->
                Type typeArgument = typeArguments[0];

                if (typeArgument instanceof ParameterizedType) {<!-- -->
                    return true;
                } else if (typeArgument instanceof Class) {<!-- -->
                    return false;
                }
            }
        }

        return false;
    }

    /**
     * Get data by id
     * @param beanContext
     * @return
     */
    public abstract Result<T> getDocVo(BeanContext beanContext);

    @Override
    public List<String> getDependCells() {<!-- -->
        List<String> list = new ArrayList<>();
        return list;
    }

    @Override
    protected abstract String name();

    @Override
    public List<Field> getFields() {<!-- -->
        List<Field> fields = new ArrayList<>();

        java.lang.reflect.Field[] allFields = this.clazz.getDeclaredFields();
        for (java.lang.reflect.Field fieldVo : allFields) {<!-- -->
            Class<?> type = fieldVo.getType();
            String typeName = type.getSimpleName();
            Field field = new Field(fieldVo.getName(), FieldType.valueOf(typeName));
            fields.add(field);
        }

        return fields;
    }



    /**
     * Get default data
     * @return default data
     */
    private List<Map<String, Object>> getDefaultData() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException {<!-- -->
        Constructor<E> constructor = this.clazz.getDeclaredConstructor(Boolean.class);
        E e = constructor.newInstance(true);

        List<Map<String,Object>> datas = new ArrayList<>();
        Map<String,Object> data = new HashMap<>();

        java.lang.reflect.Field[] allFields = this.clazz.getDeclaredFields();

        for (java.lang.reflect.Field field : allFields) {<!-- -->
            field.setAccessible(true);

            data.put(field.getName(), field.get(e));
        }
        datas.add(data);
        return datas;
    }
}