帆软报表填报前期步骤按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相信大家都会的。
注意:
- 由于是在spring框架的项目,我们的这些类都需要在spring.xml进行定义(<bean>),这里也不贴了。
- 数据保存或者更新时大家要多多注意,因为填报是整页提交,我们也需要在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();
}
至此全部结束,这个思想大家可以借鉴,网上帆软的帖子太少了,填报更少了,搞了两天才搞好。大家有指正的,欢迎在留言区指正;有收获的欢迎点赞。(纸上得来终觉浅,绝知此事要躬行!加油)