帆軟報表填報自定義提交-後臺數據解析保存詳解

帆軟報表填報前期步驟按api或者百度就可以完成,今天我這裏着重講解自定義提交(訪問web後臺服務進行提交)。

填報提交有兩種方式:
1)內置SQL:沒什麼難度,按教程來就行。

2)自定義提交:對應後臺服務進行數據處理保存。

自定義提交大致分爲以下幾個步驟:

1)編寫自定義帆軟接收類(上圖的Fill類)

自定義接收類我這裏以ReportFillDataHandler類爲例,繼承TotalSubmitJob(可以一次處理多行數據),話不多說直接上代碼

public class ReportFillDataHandler extends TotalSubmitJob{

    private static final String SUFFIN_IN = "-NN";
    protected JobValue hiddenLineNum;// 指定不保存入庫的行號所存放的單元格,單元格中行號以逗號分隔,如:5,13
    protected JobValue sortTable;   //指定表執行的先後順序,格式:類名-順序編號,從小到大排序,以逗號分隔
    protected boolean removeRepeat; //是否去點重複數據
    protected List<Integer> delIndex = new ArrayList<>();   //紀錄刪除行索引
    protected List<String> flagNameList;    //保存定義成“單元格”或“常量”變量名,不再讀取數據
    protected HashMap<String, String> hideColMap = new HashMap<String, String>();
    protected boolean removeNotNull;    //對於propertyCode-NN形式的非空字段,如果值爲空則忽略掉
    private static ApplicationContext ac;

    static {
        ServletContext servletContext = ContextLoader.getCurrentWebApplicationContext().getServletContext();
        ac = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
    }

    @Override
    protected void doTotalJob(Data data, Calculator calculator) throws Exception {
        //初始化參數
        initPara();
        //初始化flagNameList
        flagNameList = new ArrayList<String>();
        flagNameList.add("hiddenLineNum");
        flagNameList.add("SortTable");
        flagNameList.add("removeRepeat");
        flagNameList.add("removeNotNull");
        //獲取隱藏行號
        if (hiddenLineNum != null && !"".equals(hiddenLineNum.getValue())) {
            for (String hstr: hiddenLineNum.getValue().toString().split(",")) {
                hideColMap.put(hstr,hstr);
            }
        }
        //預處理
        preProcess(data);
        //讀取數據集
        Map<String,List<Map<String,String>>> sheets = new HashMap<>();//報表數據集,格式:<表名,行列名[<字段名,字段值>]>
        try {
            readDataSet(sheets,data);
        } catch (Exception ex) {
            ex.printStackTrace();
            throw new WriteSubmitException("保存失敗:" + ex.getMessage());
        }
        //表排序,用sortTable中帶有序號的表名替換原表名
        if (sortTable != null && !"".equals(sortTable.getValue())) {
            String[] tableSorts = sortTable.getValue().toString().split(",");
            String[] tbAndNum = null;
            for (String tableSort: tableSorts) {
                if (tableSort == null && "".equals(tableSort)) {
                    continue;
                }
                tbAndNum = tableSort.split("-");
                if (tbAndNum.length < 2) {
                    continue;
                }
                if (sheets.get(tbAndNum[0]) != null) {
                    sheets.put(tableSort,sheets.get(tbAndNum[0]));
                    sheets.remove(tbAndNum[0]);//去除原數據
                }
            }
        }
        //去重
        if (removeRepeat) {
            sheets = getNewSheetMap(sheets);
        }
        //去除非空字段爲null的行
        if (removeNotNull) {
            sheets = removeNullField(sheets);
        }
        //其他處理
        sheets = afterProcess(sheets);
        System.out.println("最終數據:"+sheets.toString());
        String result = JSON.toJSONString(sheets);
        System.out.println("最終數據jsonResult:"+result);

        //調用入庫服務接口---開始後臺數據保存
        ReportFillUtils reportFillUtils = (ReportFillUtils) ac.getBean("reportcenter.utils.reportFillUtils");
        reportFillUtils.reportDataCommit(sheets);
    }
    /**
     * 初始化公共參數,由於帆軟不能實例化父類中的JobValue,故提供子類給父類賦值的變通方式
     */
    protected void initPara() {}
    /**
     * 解析獲取數據集前進行處理,供子類覆蓋實現過程
     */
    private void preProcess(Data data) {}
    /** 從AbstractTotalJob.Data讀取數據到Map中,這是默認實現,如有特殊需求在子類覆蓋即可
     * @param sheets
     * @param data
     */
    private void readDataSet(Map<String, List<Map<String, String>>> sheets, Data data) throws Exception {
        List<Map<String,String>> rows;  //行數據
        String[] keyArray;  //表名和字段名的拆分數組
        String returnRes = "";
        for (int i = 0; i < data.getColumnCount(); i++) {
            //如果是標識列就跳過
            if (flagNameList.indexOf(data.getColumnName(i)) > -1) {
                continue;
            }
            //字段屬性名必須包含-
            keyArray = data.getColumnName(i).toString().split("-");
            if (keyArray.length < 2) {
                throw new Exception("模板列" + data.getColumnName(i).toString() + "配置格式不正確");
            }
            if (!sheets.containsKey(keyArray[0])) {
                rows = new ArrayList<Map<String,String>>();
                sheets.put(keyArray[0],rows);
            } else {
                rows = sheets.get(keyArray[0]);
            }
            returnRes = fullRowsByColumn(data,i,rows);
            if (!"".equals(returnRes)) {
                throw new Exception(returnRes);
            }

        }
    }
    /** 根據某列填充行信息的默認實現,子類可重寫*/
    private String fullRowsByColumn(Data data, int column, List<Map<String, String>> rows) {
        try {
            HashMap tempHideMap = (HashMap) hideColMap.clone();
            Map row;
            if (rows.size() == 0) {
                for (int i = 0; i < data.getRowCount(); i++) {
                    //判斷是否進行行刪除
                    if (!delIndex.isEmpty() && delIndex.indexOf(i) > -1) {
                        continue;
                    }
                    Object value = data.getValueAt(i, column);
                    if (value instanceof JobValue) {
                        JobValue ce = (JobValue) value;
                        if (ce.getValue() instanceof FArray) {
                            FArray valueList = (FArray) ce.getValue();
                            System.out.println("rows:" + rows.size() + "  valueList:" + valueList.length() + "第" + column + "列: " + valueList.toString());
                            System.out.print("插入記錄:");
                            for (int j = 0; j < valueList.length(); j++) {
                                if (tempHideMap.containsKey("" + j)) {
                                    tempHideMap.remove("" + j);
                                    continue;
                                }
                                row = new HashMap();
                                System.out.print("{" + data.getColumnName(column).substring(data.getColumnName(column).indexOf("-") + 1) + ":" + valueList.elementAt(j) + "} ");
                                if (valueList.elementAt(j) != null) {
                                    row.put(data.getColumnName(column).substring(data.getColumnName(column).indexOf("-")+1),valueList.elementAt(j).toString());
                                } else {
                                    row.put(data.getColumnName(column).substring(data.getColumnName(column).indexOf("-")+1),"");
                                }
                                rows.add(row);
                            }
                        } else {
                            row = new HashMap();
                            row.put(data.getColumnName(column).substring(data.getColumnName(column).indexOf("-")+1),ce.getValue().toString());
                            rows.add(row);
                        }
                    }
                }
            } else {
                int index = 0;
                for (int i = 0; i < data.getRowCount(); i++) {
                    //判斷是否進行行刪除
                    if (!delIndex.isEmpty() && delIndex.indexOf(i) > -1) {
                        continue;
                    }
                    Object value = data.getValueAt(i, column);
                    if (value instanceof JobValue) {
                        JobValue ce = (JobValue) value;
                        if (ce.getValue() instanceof FArray) {
                            FArray valueList = (FArray) ce.getValue();
                            System.out.println("rows:" + rows.size() + "  valueList:" + valueList.length() + "第" + column + "列: " + valueList.toString());
                            System.out.print("插入記錄:");
                            for (int j = 0; j < valueList.length(); j++) {
                                if (tempHideMap.containsKey("" + j)) {
                                    tempHideMap.remove("" + j);
                                    continue;
                                }
                                if (index < rows.size()) {
                                    if (valueList.elementAt(j) != null) {
                                        rows.get(index).put(data.getColumnName(column).substring(data.getColumnName(column).indexOf("-") + 1), valueList.elementAt(j).toString());
                                    } else {
                                        rows.get(index).put(data.getColumnName(column).substring(data.getColumnName(column).indexOf("-") + 1), "");
                                    }
                                    index++;
                                } else {
                                    break;
                                }
                            }
                        } else {
                            if (index < rows.size()) {
                                rows.get(index).put(data.getColumnName(column).substring(data.getColumnName(column).indexOf("-") + 1), ce.getValue().toString());
                                index++;
                            } else {
                                break;
                            }
                        }
                    }
                }
            }
            return "";
        }   catch (Exception ex) {
            return "獲取行數據失敗" + ex.getMessage();
        }
    }
    /**
     * 解析獲取數據集後進行處理,供子類覆蓋實現過程
     */
    private Map<String, List<Map<String, String>>> afterProcess(Map<String, List<Map<String, String>>> sheets) {
        return sheets;
    }
    //去除非空字段爲null的行
    private Map<String, List<Map<String, String>>> removeNullField(Map<String, List<Map<String, String>>> sheets) {
        Map newSheet = new HashMap();
        for (Map.Entry<String,List<Map<String, String>>>  entry:sheets.entrySet()) {
            String entityCode = entry.getKey();
            Set<Map<String,String>> tmpRows = new HashSet<>(entry.getValue());
            List<Map<String, String>> newRows = new ArrayList<Map<String, String>>();
            boolean remove;
            for (Map<String,String> row:tmpRows) {
                remove = false;
                Map<String, String> newRow=new HashMap<String, String>();
                //對於propertyCode-NN形式的非空字段,如果值爲空則忽略掉
                for (Map.Entry<String, String> field:row.entrySet()) {
                    String key = field.getKey();
                    String value=String.valueOf(field.getValue());
                    if (key.endsWith(SUFFIN_IN)) {
                        if (StringUtils.isEmpty(value)) {
                            remove = true;
                            break;
                        } else {
                            newRow.put(key.substring(0,key.length()-3),value);
                        }
                    } else {
                        newRow.put(key,value);
                    }
                }
                if (!remove) {
                    newRows.add(newRow);
                }
            }
            //如果有數據才放入Map
            if (!newRows.isEmpty()) {
                newSheet.put(entityCode, newRows);
            }
        }
        return newSheet;
    }
    //去除重複的行
    private Map<String, List<Map<String, String>>> getNewSheetMap(Map<String, List<Map<String, String>>> sheets) {
        Map newSheet = new HashMap();
        Iterator<Map.Entry<String, List<Map<String, String>>>> iterator = sheets.entrySet().iterator();
        while (iterator.hasNext()) {
            List newRow = new ArrayList<>();
            Map.Entry<String, List<Map<String, String>>> entry = iterator.next();
            String key = entry.getKey();
            List<Map<String, String>> list = entry.getValue();
            for (int i = 0; i < list.size(); i++) {
                if (newRow.indexOf(list.get(i)) > -1) {
                    continue;
                } else {
                    newRow.add(list.get(i));
                }
            }
            newSheet.put(key,newRow);
        }
        return newSheet;
    }
}

       上面代碼中封裝了好多方法,諸如是否刪除行,是否刪除重複數據,以及非空處理等等,大家可以各取所需;個人感覺在入庫保存前的代碼是可以複用的,只是接下來的名字規則,與後臺保存可能會有部分差異。

2)在填報屬性界面中,添加對應的屬性(這裏的名字規則,尤爲重要,下文詳細講)

       由於填報時我們需要把對應實體以及實體屬性存放在cpt中,所以我們這裏設定了“實體名-屬性名”,作爲報表單元格的屬性,

並在上面(1)中以Map<String, List<Map<String, String>>>方式保存單元格數據,map對應的key即爲我們的實體類名。

3)編寫後臺數據保存代碼

         上面兩步我們已經獲得了Map<String, List<Map<String, String>>>方式保存的單元格數據,我們此時需要根據key實例化我們的實體類,而key只是字符串名稱,因此可以用反射獲取對應實例,Class.forName(name),又有個問題name是全限定名,我們只有單單類名,這裏我們項目實體類都在一個包下,是固定的我就直接寫死了;還有service也是同理,獲取實例調用更新或者保存方法進行數據的提交。下面直接上代碼:

public class ReportFillUtils {
    private static final String entityPath = "com.reportcenter.business.entity.";
    private static final String servicePath = "com.reportcenter.productSum.service.impl.";
    @Autowired
    @Qualifier("reportcenter.productSum.proGasSumDao")
    private  IProGasSumDAO proGasSumDAO;

    //對填報的數據進行存儲
    public  String reportDataCommit(Map<String,List<Map<String,String>>> sheets) {
        if (sheets != null && sheets.size() > 0) {
            for (Map.Entry<String, List<Map<String,String>>> sheet : sheets.entrySet()) {
                String project = sheet.getKey();
                List<Map<String, String>> entityList = sheet.getValue();
                String entityName = project+"Entity";   //獲取實體類名稱
                String serviceName = project+"ServiceImpl"; //獲取服務類名稱
                List rowInsertList = new ArrayList<>();
                List rowUpdateList = new ArrayList<>();
                try {
                    //獲取對應實體類
                    Class<?> clazz = Class.forName(entityPath + entityName);
                    if (entityList != null && entityList.size() > 0) {
                        for (Map entityMap:entityList) {
                            //將map中的數據填充到實體中
                            Object entity = Utils.mapToEntity(entityMap, clazz);
                            if (entityMap.containsKey("identifier")) {
                                rowUpdateList.add(entity);
                            } else {
                                rowInsertList.add(entity);
                            }
                        }
                        //實例化服務類
                        Class<?> serviceClass = Class.forName(servicePath + serviceName);
                        Object serviceEntity = serviceClass.newInstance();
                        //由於反射獲取的實例,並不會注入對應的屬性,因此我們需要自己注入
                        Method setProGasSumDAO = serviceClass.getMethod("setProGasSumDAO", IProGasSumDAO.class);
                        if (setProGasSumDAO != null) {
                            setProGasSumDAO.invoke(serviceEntity,proGasSumDAO);
                        }
                        if (rowInsertList.size() > 0) {
                            Method insert = serviceEntity.getClass().getMethod("insertFromReport",List.class);
                            Object invoke = insert.invoke(serviceEntity,rowInsertList);
                        }
                        if (rowUpdateList.size() > 0) {
                            Method update = serviceEntity.getClass().getMethod("updateFromReport", List.class);
                            Object invoke = update.invoke(serviceEntity,rowUpdateList);
                        }

                    }
                } catch (Exception e) {
                    return e.getMessage();
                }
            }
        }
        return null;
    }
}
public class Utils {

    //實體類轉換爲map
    public static HashMap<String, Object> convertToMap(Object obj)
            throws Exception {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        HashMap<String, Object> map = new HashMap<String, Object>();
        Field[] fields = obj.getClass().getDeclaredFields();
        for (int i = 0, len = fields.length; i < len; i++) {
            String varName = fields[i].getName();
            boolean accessFlag = fields[i].isAccessible();
            fields[i].setAccessible(true);
            Object o = fields[i].get(obj);
            if (o != null) {
                if ( o instanceof Date){
                    String str= sdf.format((Date)o);
                    map.put(varName,str);
                    continue;
                }
                map.put(varName, o.toString());

                fields[i].setAccessible(accessFlag);
            }

        }
        return map;
    }

    //map轉換爲實體類
    public static <T> T mapToEntity(Map<String,Object> map,Class<T> entity) throws Exception{
        T t = entity.newInstance();
        BeanInfo beanInfo = Introspector.getBeanInfo(t.getClass());
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor field:propertyDescriptors) {
            String key = field.getName();
            if (map.containsKey(key)) {
                Object obj = map.get(field.getName());
                Method setter = field.getWriteMethod();
                if (obj != null && !obj.toString().equals("")) {
                    if (field.getPropertyType().toString().equals("class java.lang.String")) {
                        setter.invoke(t,obj);
                    } else if (field.getPropertyType().toString().equals("class java.lang.Integer") || field.getPropertyType().toString().equals("int")) {
                        setter.invoke(t,Integer.valueOf(obj.toString()));
                    } else if (field.getPropertyType().toString().equals("class java.lang.Double") || field.getPropertyType().toString().equals("double")) {
                        setter.invoke(t,Double.valueOf(obj.toString()));
                    } else if (field.getPropertyType().toString().equals("class java.util.Date")) {
                        setter.invoke(t,DateFormatUtils.parse(obj.toString()));
                    } else if (field.getPropertyType().toString().equals("class java.lang.Long") || field.getPropertyType().toString().equals("long")) {
                        setter.invoke(t,Boolean.valueOf(obj.toString()));
                    } else if (field.getPropertyType().toString().equals("class java.lang.Boolean") || field.getPropertyType().toString().equals("boolean")) {
                        setter.invoke(t,Boolean.valueOf(obj.toString()));
                    }
                }
            }
        }
        return t;
    }
}

上面就是報表填報--》數據解析--》數據保存的代碼了,最後保存和更新的服務這裏就不上了CRUD相信大家都會的。

注意:

  1. 由於是在spring框架的項目,我們的這些類都需要在spring.xml進行定義(<bean>),這裏也不貼了。
  2. 數據保存或者更新時大家要多多注意,因爲填報是整頁提交,我們也需要在dao一次提交更新,一條一條更新,那樣填報一次耗時太長,也需要多次獲取數據庫連接,體驗太差。(下面我貼下我更新的代碼,可參考)
// 繼承了SqlMapClientDaoSupport
 public void insertFromReport(String statementName, List list) throws Exception {
        SqlMapClient sqlMapClient = getSqlMapClient();
        sqlMapClient.startBatch();
        for (Object entity:list) {
            sqlMapClient.insert(statementName,entity);
        }
        //一次提交入庫
        sqlMapClient.executeBatch();
    }

至此全部結束,這個思想大家可以借鑑,網上帆軟的帖子太少了,填報更少了,搞了兩天才搞好。大家有指正的,歡迎在留言區指正;有收穫的歡迎點贊。(紙上得來終覺淺,絕知此事要躬行!加油)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章