在前端点击打印后,需要根据参数值、模板、数据源
实现解析数据源、生成SQL、读取数据源、解析word模板、识别占位符与约定标识符、识别模板样式(字体尺寸颜色等)、填充数据。
用来保存前端配置的表结构大概这样


前端提交的配置数据类似这样【完整格式参考
{
	template:1						 //模板
	datasources:[{                   //数据源
		name:'数据源名称'
		tables:[{
			table:'hr_department'    //数据源-关联表名
			join:'left'              //连接方式(inner:内连接, left:左连接 ,right:右连接)
			relations:[{             //关联条件
				join:'and'                         //关联方式(可选and,or,ors)
				column:'id'                        //列名
				compare:10                         //比较运算符
				value:null                         //常量值或变量(二选一)
				relattion_table:'hr_employee'      //比较表名(二选一)
				relattion_column:'department_id'   //比较列名
			}] //end-relattions
		}] //end-tables
		
		,conditions:[{                             //过滤条件
			join:'and'
			table:'hr_employee'
			column:'type_id'
			compare:10
			value:100							    //常量值或变量
		}]//end-conditions
		,havings:[{									//分组过滤条件
			join:'and'
			method:'sum'
			table:'hr_employee'
			column:'type_id'
			compare:10
			value:100							    //常量值或变量
		}]//end-havings
		,orders:[{						   			//排序
			table:'hr_employee'
			column:'type_id'
			type:'ASC'
		}]
		,groups:[{						   			//分组
			table:'hr_employee'
			column:'type_id'
		}]
	}] //end-datasources
}
把以上参数保存到各自的表中。
关于于常量值或变量
常量就不用说了配置时直接提供值
变量分内置变量和配置变量
内置变量应该提交约定好
一般有两类
1.后台约定或数据库约定,如{id}或{pk}表示主表的主键
2.前端约定,一般固定在url参数中附带,如打印时url中要带一个id=123参数来定位要打印哪行数据, {param.id}就表示那个123
配置变量比较灵活,与前端约定的区别是,这里的变量名一般由用户指定


在填充模板时,前端会提交一个参数用来定位数据,如id=123
接下来根据参数123和配置项从数据库中提取数据。
1.组装SQL
2.执行SQL获取结果集(如果只有一个数据库源(库),这一步就执行一个SQL就完事了)
3.解析word中的占位符,根据占位符从结果集中提取数据
4.替换占位答生成新的word


组装SQL的细节比较多,主要是关联条件与过滤条件的组合,这里我们通过一个ConfigStore来实现,参考【ConfigStore
Map<String, DataSet> values = new HashMap<>();
//多个数据源
DataSet ds = service.querys("t_ds_datasource", "template_id:"+template.getId());
if(!ds.isEmpty()) {
	//数据源关联的全部数据
	//所有的关联条件和过渡条件一次查出 避免重复查询数据库
	DataSet all_tables = service.querys("t_ds_datasource_table", new DefaultConfigStore().in("DATASOURCE_ID", ds.getStrings("ID")));
	DataSet all_relations = service.querys("t_ds_datasource_table_relation", new DefaultConfigStore().in("DATASOURCE_ID", ds.getStrings("ID")));
	DataSet all_filters = service.querys("t_ds_datasource_filter", new DefaultConfigStore().in("DATASOURCE_ID", ds.getStrings("ID")));
	for(DataRow datasource:ds){
		//每个 数据组组装一条SQL 返回一个结果集
		ConfigStore configs = new DefaultConfigStore();
		String masterTableName = datasource.getString("TABLE_NAME");
		String masterTableAlias = datasource.getString( "TABLE_ALIAS");
		if(BasicUtil.isEmpty(masterTableAlias)){
			masterTableAlias = masterTableName;
		}
		//TableBuilder实现多表关联
		org.anyline.data.param.TableBuilder builder = org.anyline.data.param.TableBuilder.init(masterTableName +" AS "+ masterTableAlias);

		//需要查询的列
		List<String> columns = new ArrayList<>();
		DataSet tables = all_tables.getRows("DATASOURCE_ID", datasource.getId());
		if(tables.size() == 0){
			//如果只有一个表直接查*省了查询表结构的过程
			columns.add("*");
		}else{
			//如果有多个表,一般会出现列重名的情况,所以需要一个前缀来区分(这里用表名或别名__)
			List<String> cols = service.columns(masterTableName);
			for(String col:cols){
				columns.add(masterTableName+"."+col + " AS " + masterTableAlias + "__" + col);
			}
			for(DataRow table:tables){
				String tableName = table.getString( "NAME");
				//有别名的用别名,没有别名的用原表名
				String tableAlias = table.getString( "ALIAS", "NAME");
				cols = biz.columns(tableName);
				for(String col:cols){
					//添加前缀 避免列重名
					columns.add(tableName+"."+col + " AS " + tableAlias + "__" + col);
				}
			}
		}
		//每个表参考主表重复上面的过程
		for(DataRow table:tables){
			String condition = null;
			String tableName = table.getString("NAME");
			String tableAlias = table.getString( "ALIAS");
			if(BasicUtil.isEmpty(tableAlias)){
				tableAlias = tableName;
			}
			//最关键的ConfigStore需要熟悉
			ConfigStore conditions = new DefaultConfigStore();
			//与数据源和表相关的 关联条件
			DataSet relations = all_relations.getRows("DATASOURCE_ID", datasource.getId(), "TABLE_ID", table.getId());
			//关联条件
			for(DataRow relation:relations){
				String columnName = relation.getString("COLUMN_NAME");
				String value = relation.getString("VAL");
				String join = relation.getString("JOIN_CODE");
				Compare compare = compare(relation.getInt("COMPARE_CODE", 10));
				String relationTableName = relation.getString("RELATION_TABLE_NAME");
				String relationTableAlias = relation.getString("RELATION_TABLE_ALIAS", "RELATION_TABLE_NAME");
				String relationColumnNmae = relation.getString("RELATION_COLUMN_NAME");
				if(BasicUtil.isEmpty(value)) {
					//两个表之间关联
					conditions.condition(join, compare, concat(tableAlias, columnName)
							,  "${"+concat(relationTableAlias, relationColumnNmae)+"}");
				}else{
					//常量值
					conditions.condition(join, compare, concat(tableAlias, columnName), value);
				}
			}
			//生成ON关联条件
			ConditionChain chain = conditions.getConfigChain().createAutoConditionChain();
			condition = chain.getRunText(false, null, RuntimeHolder.getRuntime());

			//把占位值添加到SQL主体中
			List<RunValue> vals = chain.getRunValues();
			for(RunValue runValue:vals){
				configs.addStaticValue(runValue.getValue());
			}
			//left join/right join
			Join.TYPE join = Join.TYPE.valueOf(table.getString("JOIN_CODE").toUpperCase());
			builder.join(join,  tableName, condition);
		}
		//过滤条件
		DataSet filters = all_filters.getRows("DATASOURCE_ID", datasource.getId());
		for(DataRow filter:filters){
			String value = filter.getString("VAL");
			String join = filter.getString("JOIN_CODE");
			String tableName = filter.getString("TABLE_NAME");
			String columnName = filter.getString("COLUMN_NAME");
			Compare compare = compare(filter.getInt("COMPARE_CODE", 10));
			configs.condition(join, compare, concat(tableName, columnName), value);
		}
		configs.columns(columns);
		configs.and(masterTableAlias+".id", pv);
		DataSet set = biz.querys(builder.build(), configs);
		values.put(datasource.getCode(), set);
	}
}

现在所有的结果集已经放在了values中,
下一步解析word中的占位符,
主要通过WDocument类来实现提取占位符,操作表格等
更复杂的情况参考【aol标签
//模板文件
WDocument doc = new WDocument(tmp_file);
doc.variable("数据源名称", "查询结果集");
doc.variables(values);//或者放一个map
doc.save();
doc.save()后就把最终的格式保存到了相关的word文件,下一步把文件提交给minio服务器,再把新的url交给前端预览就可以了 更复杂的场景就需要更复杂的约定规则,但是对于后台来说主要还是用到CofigStore,WDocument两个来类就可以了。