上一篇讲了动态表单实现的基本原理,这一篇主要写一下实现的关键源码。
首先是流程定义的controller,这个类接收用户启动流程的请求,有一个流程定义id的参数。
@Controller
@RequestMapping(value = "/backstage/workflow/hi/")
public class ProcDefController extends BaseController<Object>{
@Autowired
private ProcDefService procDefService;
@Autowired
private RepositoryService repositoryService;
@Autowired
private FormService formService;
@Autowired
private IdentityService identityService;
@Autowired
private ProcInstService procInstService;
/**
* 发起流程页面
*/
@RequestMapping(value = "index")
public String index(Model model) {
List<ProcDef> procDefList = procDefService.getProcDefList();
model.addAttribute("procDefList", procDefList);
return "/system/workflow/hi/procDefList";
}
/**
* 发起指定流程表单页面
*/
@RequestMapping(value = "getStartPage")
public String getStartPage(Model model,String procDefId,RedirectAttributes attr) {
ProcDef pd = procDefService.getProcDefById(procDefId);
String key = pd.getKey();
if(key.indexOf("c_") == 0){//普通表单
return getStartPageForCommon(model,pd,attr);
}else if(key.indexOf("ex_") == 0){//外置表单
return getStartPageForEX(model,pd,attr);
}else{//动态表单
return getStartPageForDynamics(model,pd,attr);
}
}
/**
* 获取 动态表单 流程启动页面
*/
public String getStartPageForDynamics(Model model,ProcDef pd,RedirectAttributes attr){
List<FormAttr> formAttrlist = procDefService.getFormAttrList(pd);
model.addAttribute("deploymentId", pd.getDeploymentId());
model.addAttribute("pd", pd);
model.addAttribute("formAttrlist", formAttrlist);
return "/system/workflow/hi/start";
}
}
ProcDef pd = procDefService.getProcDefById(procDefId);
这个是根据流程定义id,获取对应的实体。我使用的mybatis,这里只写一下对应的xml定义:
ProcDefMapper.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.pl.repository.workflow.hi.ProcDefDao">
<resultMap id="procDef" type="ProcDef">
<id column="id" jdbcType="VARCHAR" property="id"/>
<result column= "name" property="name" />
<result column= "key" property="key" />
<result column= "version" property="version" />
<result column= "category" property="category" />
<result column= "deploymentId" property="deploymentId" />
<result column= "resourceName" property="resourceName" />
<result column= "dgrmResourceName" property="dgrmResourceName" />
<result column= "description" property="description" />
<association property="byteArray" resultMap="com.pl.repository.workflow.hi.ByteArrayDao.byteArray" columnPrefix="ba_"/>
</resultMap>
<!-- 通过id获取对象 -->
<select id="getEntityById" resultMap="procDef" parameterType="String">
select pd.id_ as id ,pd.name_ as name ,pd.key_ as key ,pd.version_ as version ,pd.category_ as category,pd.deployment_id_ as deploymentId,
pd.resource_name_ as resourceName ,pd.dgrm_resource_name_ as dgrmResourceName ,pd.description_ as description
,ba.id_ as ba_id ,ba.rev_ as ba_rev ,ba.name_ as ba_name ,ba.deployment_id_ as ba_deploymentId ,ba.bytes_ as ba_bytes
from ACT_RE_PROCDEF pd
left join ACT_GE_BYTEARRAY ba on pd.deployment_id_=ba.deployment_id_ and pd.resource_name_=ba.name_
where pd.id_ = #{id}
</select>
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.pl.repository.workflow.hi.ByteArrayDao">
<resultMap id="byteArray" type="ByteArray">
<id column="id" jdbcType="VARCHAR" property="id"/>
<result column= "rev" property="rev" />
<result column= "name" property="name" />
<result column= "deploymentId" property="deploymentId" />
<result column= "bytes" property="bytes" />
</resultMap>
</mapper>
ACT_RE_PROCDEF表与 ACT_GE_BYTEARRAY表关联,流程定义id,可以获取流程定义对应的xml文件,即ACT_GE_BYTEARRAY的 bytes_字段。
我这里用key的命名规则区分了下表单类型,c_前缀开始表示普通表单,ex_开始表示外置表单,其他的都是动态表单。
List<FormAttr> formAttrlist = procDefService.getFormAttrList(pd);
这个方法对应 前一篇文章中原理所讲的4.1和4.2步骤。即或者xml并解析。
ProcDefService.java
@Service("procDefService")
public class ProcDefService extends BaseServiceImp<ProcDef> {
@Autowired
private ProcDefDao procDefDao;
public List<FormAttr> getFormAttrList(ProcDef pd) {
ByteArray ba = pd.getByteArray();
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
outStream.write(ba.getBytes(), 0, ba.getBytes().length);
String xml = null;
try {
xml = new String(ba.getBytes(), "utf-8");//读取数据库中xml文件,二进制格式转换为字符串格式
} catch (UnsupportedEncodingException e) {
logger.error(e.getMessage());
e.printStackTrace();
}
Node root = XMLTools.Dom2Map(xml);//使用dom4j解析xml文件
List<FormAttr> formMap = new ArrayList<FormAttr>();
Node processNode = root.getSubNodeByName("process");
Node startEventNode = processNode.getSubNodeByName("startEvent");
Node extensionElements = startEventNode.getSubNodeByName("extensionElements");
if(extensionElements == null){
return formMap;
}
List<Node> formPropertyList = extensionElements.getChildrenList();
for (Node formProperty : formPropertyList) {
FormAttr fa = new FormAttr(formProperty);
formMap.add(fa);
}
return formMap;
}
}
/***
* JSON、XML、MAP的互转
* @author pelin
*/
public class XMLTools {
/***
* 将dom4j的Element格式化为自己定义的结点Node格式
* @param Node
* @return
*/
public static Node getNodes(Element ele){
Node node = new Node();
Map<String, String> attrMap = new HashMap<String, String>();
List<Node> childrenList = new ArrayList<Node>();
node.setName(ele.getName());//当前节点名称
node.setText(ele.getTextTrim());//当前节点文本内容
node.setAttrMap(attrMap);//当前节点所有属性的list
node.setChildrenList(childrenList);//当前节点所有子节点
List<Attribute> listAttr = ele.attributes();//当前节点的所有属性的list
for(Attribute attr:listAttr){//遍历当前节点的所有属性
attrMap.put(attr.getName(), attr.getValue());
}
//递归遍历当前节点所有的子节点
List<Element> listElement = ele.elements();//所有一级子节点的list
for(Element e:listElement){//遍历所有一级子节点
Node mapChildren = getNodes(e);//递归
childrenList.add(mapChildren);
}
return node;
}
public static Node Dom2Map(String xml){
Document doc = null;
Element root= null;
try {
doc = DocumentHelper.parseText(xml);
root = doc.getRootElement();//获取根节点
} catch (DocumentException e) {
e.printStackTrace();
}
return getNodes(root);
}
}
public class Node {
String name;
String text;
Map<String, String> attrMap;
List<Node> childrenList;
}
/***
* 表单属性
* @author Administrator
*/
public class FormAttr {
String id;
String name;
String type;
boolean required;//默认是fasle
boolean readable;//默认是true
boolean writable;//默认是true
List<Node> selects;
public FormAttr(){}
public FormAttr(Node formProperty){
Map<String,String> attrMap = formProperty.getAttrMap();
id = attrMap.get("id");
name = attrMap.get("name");
type = attrMap.get("type");
//attrMap没找到,即结果为null说明是默认值
required = "true".equals(attrMap.get("required")) ? true : false;
readable = "false".equals(attrMap.get("readable")) ? false : true;
writable = "false".equals(attrMap.get("writable")) ? false : true;
if("enum".equals(type)){
selects = formProperty.getChildrenList();
}
}
public String toRequiredStr(){
return required ? " required='true' " : "";
}
public String toReadableStr(){
return readable ? "" : " display:none; ";
}
public String toWritableStr(){
return writable ? " name='"+id+"' " : " readonly='readonly' ";
}
}
通过上面的处理,可以获取到流程定义时开始结点的表单属性列表了。接下来只剩下最后一步4.3. 即 根据表单属性列表生成相应的html代码。
具体实现源码,见下一篇。