20Demo01 一个基于HXFlow的Demo

来源:互联网 发布:2015公安部上牌数据 编辑:程序博客网 时间:2024/06/05 14:22

这是一个基于HXFlow的小demo


首先看一下流程图吧



这个业务, 也算是比较简单


1. 需求就是zhangsan填写申请记录, 然后提交到RM审批

2. 然后zhangsansan可以退回给zhangsan, 或者拒绝这个请求, 或者审批通过然后交给上一级RGM审核

3. 然后zhangsi可以将申请退回给shangsan, 或者拒绝这个请求, 或者审批通过, 正常流程结束


效果截图 [我去 gif画风崩了]


状态切换1 : picApply - rmAuth - rgmAuth - success

          accept  accept   accept


状态切换2 : picApply - rmAuth - rgmAuth - failed

         accept   accept  reject



状态切换3 : picApply - rmAuth - picApply

         accept   return






好了, 分享一下核心代码, 但是 项目依赖于HXLog, HXAttrHandler, HXMongo, 然后这三个项目是找不到的[有空分享出来], 因此 就当随便看看吧

这里有一些为了实现目的而妥协的地方, 比如 loadFlowInstance的使用, 因为 这是三个进程, 然后 都有各自的flowEngine, 因此 当给定的用户执行命令之前从新从db中load了一次该用户的所有task, 并加载到flowEngine中, 这样的实现不完善, 而且 还存在潜在的隐患

多个pic申请任务的时候id的冲突的解决问题[TaskIdGenerator, 更新taskId的生成策略]

某些地方异常处理的漏洞 等等

代码 也算是比较简单, 这里就不多解释了


Test01HXFlowUsage

/**
 * file name : Test01HXFlowUsage.java
 * created at : 19:14  2017-03-22
 * created by 970655147
 */
public class Test01HXFlowUsage {

    
/**
     * 
一些上下文信息
     */
    // 辅助输入的工具
    
public static final Scanner SCANNER new Scanner(System.in);
    
// 记录当前登陆的用户的信息
    
public static User LOGGED_USER null;
    
public static MysqlTaskDao DAO new MysqlTaskDaoImpl(Task.PROTO_BEANnew MysqlDbConfig()
            .username(
"root").password("root")
            .db(
"test02").table("hx_flow_task").id("id"));
    
public static final FlowEngine<State, Action> FLOW_ENGINE new StandardFlowEngine();
    
public static final String TEST_FLOW_NAME "jerry";

    
static {
        StandardStateMachine.TransferMapBuilder<State, Action> builder = StandardStateMachine.TransferMapBuilder.start()
                .add(ApplyState.
PIC_APPLY, ApplyAction.ACCEPT, ApplyState.RM_AUTH, AcceptHandler.getInstance())
                .add(ApplyState.
RM_AUTH, ApplyAction.ACCEPT, ApplyState.RGM_AUTH, AcceptHandler.getInstance())
                .add(ApplyState.
RGM_AUTH, ApplyAction.ACCEPT, ApplyState.SUCCESS, AcceptHandler.getInstance())
                .add(ApplyState.
RM_AUTH, ApplyAction.RETURN, ApplyState.PIC_APPLY, ReturnHandler.getInstance())
                .add(ApplyState.
RGM_AUTH, ApplyAction.RETURN, ApplyState.PIC_APPLY, ReturnHandler.getInstance())
                .add(ApplyState.
RM_AUTH, ApplyAction.REJECT, ApplyState.FAILED, RejectHandler.getInstance())
                .add(ApplyState.
RGM_AUTH, ApplyAction.REJECT, ApplyState.FAILED, RejectHandler.getInstance());
        
// pic - rm - rgm - success
        //     \  |  /
        //     failed
        
StateMachine<State, Action> stateMachine = new StandardStateMachine(ApplyState.PIC_APPLY, builder.build());
        
FLOW_ENGINE.deploy(TEST_FLOW_NAME, stateMachine);
    }

    
/**
     * 
业务所需常量
     */
    /**
     * 模拟的命令
     */
    
public static final String LS "ls";
    
public static final String APPLY_TASK "apply";
    
public static final String DO "do";
    
public static final String HELP "help";
    
public static final String QUIT "quit";
    
public static final Map<String, CmdExecutor> CMD_2_EXECUTOR = Tools.asMap(new String[]{
            
LSAPPLY_TASKDOHELP
    
}, new LsCmdExecutor(), new ApplyCmdExecutor(), new DoCmdExecutor(), new HelpCmdExecutor());

    
/**
     * 
三个测试用户
     */
    
public static final User RGM new User("rgm""rgm", Role.RGMnull);
    
public static final User RM new User("rm""rm", Role.RMRGM.getUserName());
    
public static final User PIC new User("pic""pic", Role.PICRM.getUserName());
    
public static final Map<String, User> allUser = Tools.asMap(new String[]{
            
PIC.getUserName(), RM.getUserName(), RGM.getUserName()
    }, 
PICRMRGM);

    
// 以一个具体的场景模拟HXFlow的使用
    
public static void main(String[] args) throws Exception {

        
// ------------------------------------ prepare start ---------------------------------------------

        // MysqlSqlGenerator.generateCreateTableSql
的时候, 将Task声明为public, 否则 会访问不到getter
//        Task task = new Task("1", "jerry", "apply", "taskName", "taskDesc", "handler");
//        info(MysqlSqlGenerator.generateCreateTableSql("hx_flow_task", JSONObject.fromObject(task)) );
//        info(JSONTransferableUtils.generateIdxes(Task.class, 3) );
//        info(JSONTransferableUtils.generateDaoDaoImpl(Task.class, JSONTransferableUtils.TYPE_MYSQL) );

        // ------------------------------------ prepare end ---------------------------------------------

        
while (true) {
            User user = 
null;
            
while (true) {
                info(
"please input userName : ");
                String userName = 
SCANNER.nextLine();
                info(
"please input password : ");
                String pwd = 
SCANNER.nextLine();
                user = 
allUser.get(userName);
                
if ((user != null) && (user.getPwd().equals(pwd))) {
                    
break;
                }
                err(
"invalid userName or password !");
            }

            
// logged in, then deal user's input
            
LOGGED_USER 
= user;
            info(
"user " LOGGED_USER.getUserName() + " logged in !");
            
while (true) {
                info(
"please input an command, try help for more information !");
                String cmd = 
SCANNER.nextLine();
                loadFlowInstance(
LOGGED_USER);
                
if (Tools.isEmpty(cmd)) {
                    
continue;
                }
                cmd = cmd.trim().toLowerCase();
                
int idxOfBlank = cmd.indexOf(" ");
                String firstArg = (idxOfBlank >= 
0) ? cmd.substring(0, idxOfBlank) : cmd;
                
if (QUIT.equals(firstArg)) {
                    info(
"user " LOGGED_USER.getUserName() + " logged out !");
                    
break;
                }

                CmdExecutor executor = 
CMD_2_EXECUTOR.get(firstArg);
                
if (executor == null) {
                    warn(
"have no this cmd : " + cmd);
                    
continue;
                }
                executor.execute(
LOGGED_USER, cmd);
            }
        }
    }

    
/**
     * 
加载给定的用户的所有flowIntance到flowEngine
     *
     * @param 
user 需要加载的任务所属的用户
     * @return void
     * @author 970655147 created at 2017-03-22 23:42
     */
    
private static void loadFlowInstance(User user) {
        List<Task> tasks = 
null;
        
try {
            tasks = 
DAO.findMany(Criteria.eq("handler", user.getUserName()), IDX_MANAGER.getDoLoad(), null);
        } 
catch (Exception e) {
            e.printStackTrace();
            err(
"error while read user[" + user.getUserName() + "]'s tasks !");
            
return;
        }
        
for (Task task : tasks) {
            
FLOW_ENGINE.addFlowInstance(task.getId(), task.getFlow(), ApplyState.DUMMY.idOf(task.getState()), nullnull);
        }
    }


    
/**
     * 
执行命令的接口
     */
    
public static interface CmdExecutor {
        
/**
         * 
执行给定的命令
         *
         * @return
         
@author 970655147 created at 2017-03-22 20:57
         */
        
public void execute(User user, String cmd);
    }

    
/**
     * 
获取给定的用户的executor
     */
    
private static class LsCmdExecutor implements CmdExecutor {
        
@Override
        
public void execute(User user, String cmd) {
            List<Task> tasks = 
null;
            
try {
                tasks = 
DAO.findMany(Criteria.eq("handler", user.getUserName()), IDX_MANAGER.getDoLoad(), null);
            } 
catch (Exception e) {
                e.printStackTrace();
                err(
"error while read user[" + user.getUserName() + "]'s tasks !");
                
return;
            }
            infoForPage(
"all tasks as follow");
            
for (Task task : tasks) {
                info(task.toString());
            }
            infoForPage(
"all tasks ends");
        }
    }

      /**
      *
申请任务的命令executor
      */

    private static class ApplyCmdExecutor implements CmdExecutor {
        
@Override
        
public void execute(User user, String cmd) {
            
if (user.getRole() != Role.PIC) {
                err(
"can't create flow !");
                
return;
            }

            String flow = Tools.getStrInRangeWithStart(cmd, 
APPLY_TASK).trim();
            String taskId = 
FLOW_ENGINE.startFlowInstance(flow, nullnull);
            
if (Tools.isEmpty(taskId)) {
                err(
"have no this flow : " + flow);
                
return;
            }

            info(
"please input taskName : ");
            String name = 
SCANNER.nextLine();
            info(
"please input taskDesc : ");
            String desc = 
SCANNER.nextLine();
            FlowTaskFacade<State, Action> taskFacade = 
FLOW_ENGINE.getTask(taskId, null);
            Task task = 
new Task(taskId, taskFacade.flow(), taskFacade.now().id(), name, desc, user.getUserName());
            
try {
                
DAO.save(task, IDX_MANAGER.getDoLoad(), IDX_MANAGER.getDoFilter());
            } 
catch (Exception e) {
                err(
"error while save task[" + task + "] !");
            }
        }
    }

    
/**
     * 
操作任务的executor
     */
    
private static class DoCmdExecutor implements CmdExecutor {
        
@Override
        
public void execute(User user, String cmd) {
            String taskId = Tools.getStrInRangeWithStart(cmd, 
DO).trim();
            FlowTaskFacade<State, Action> task = 
FLOW_ENGINE.getTask(taskId, null);
            
if (task == null) {
                err(
"have no this task : " + taskId);
                
return;
            }

            StateMachine<State, Action> stateMachine = 
FLOW_ENGINE.getStateMachine(task.flow());
            List<Action> nextActions = stateMachine.nextActions(task.now());
            info(
"all action could apply as follow, please select one or quit");
            
for (Action action : nextActions) {
                info(action.id());
            }
            String action = 
SCANNER.nextLine();
            
if (Tools.isEmpty(action)) {
                err(
"empty action !");
                
return;
            }

            action = action.trim();
            
if (QUIT.equals(action)) {
                
return;
            }
            
try {
                
boolean succ = FLOW_ENGINE.complete(task.id(), ApplyAction.DUMMY.idOf(action), user, null);
                
if (!succ) {
                    err(
"unknown action : " + action);
                }
            } 
catch (Exception e) {
                e.printStackTrace();
                err(
"error while complete task[" + task.toString() + "], with action : " + action);
            }
        }
    }

    
/**
     * 
打印提示信息
     */
    
private static class HelpCmdExecutor implements CmdExecutor {
        
@Override
        
public void execute(User user, String cmd) {
            info(
"this is a simple tests for HXFlow !");
            info(
"  `ls` : ls current user's todo tasks !");
            info(
"  `apply $flow` : apply an task for current user !");
            info(
"  `do $taskId` : complete specified task !");
            info(
"  `help` : show help information !");
        }
    }


    
/**
     * pic - rmAuth, rmAuth -> rgmAuth, rgmAuth -> success
     * 
处理所有的accept部分的逻辑
     */
    
private static class AcceptHandler implements TransferHandler<State, Action> {
        
private static final AcceptHandler INSTANCE new AcceptHandler();

        
public static AcceptHandler getInstance() {
            
return INSTANCE;
        }

        
@Override
        
public boolean handle(TransferContext<State, Action> transferContext) throws Exception {
            User user = (User) transferContext.extra();
            IUpdateCriteria update = Criteria.set(
"state", transferContext.dstState().id()).add("handler", user.getLeader());
            
DAO.updateMany(Criteria.eq("id", transferContext.task().id()), update);
            
return true;
        }
    }

    
/**
     * rmAuth -> pic, rgmAuth -> pic
     * 
处理所有的return部分的逻辑
     */
    
private static class ReturnHandler implements TransferHandler<State, Action> {
        
private static final ReturnHandler INSTANCE new ReturnHandler();

        
public static ReturnHandler getInstance() {
            
return INSTANCE;
        }

        
@Override
        
public boolean handle(TransferContext<State, Action> transferContext) throws Exception {
            IUpdateCriteria update = Criteria.set(
"state", transferContext.dstState().id()).add("handler",PIC.getUserName());
            
DAO.updateMany(Criteria.eq("id", transferContext.task().id()), update);
            
return true;
        }
    }

    
/**
     * rmAuth -> failed, rgmAuth -> failed
     * 
处理所有的reject部分的逻辑
     */
    
private static class RejectHandler implements TransferHandler<State, Action> {
        
private static final RejectHandler INSTANCE new RejectHandler();

        
public static RejectHandler getInstance() {
            
return INSTANCE;
        }

        
@Override
        
public boolean handle(TransferContext<State, Action> transferContext) throws Exception {
            IUpdateCriteria update = Criteria.set(
"state", transferContext.dstState().id()).add("handler"null);
            
DAO.updateMany(Criteria.eq("id", transferContext.task().id()), update);
            
return true;
        }
    }

}

 


Task 

public class Task implements JSONTransferable<Task, Integer> {

    
private String id;
    
private String flow;
    
private String state;
    
private String name;
    
private String desc;
    
private String handler;

 

     // 省略其他getter & setter 以及JSONTransferable相关

 

}


User

public class User {

   
private String userName;
   
private String pwd;
   
private Role role;
   
private String leader;


 

      // 省略其他getter & setter

 

}


ApplyState

public enum ApplyState implements State<ApplyState> {
   
   
PIC_APPLY("apply"), RM_AUTH("rmAuth"), RGM_AUTH("rgmAuth"), SUCCESS("success"), FAILED("failed"), DUMMY("dummy");
   
private static Map<String, ApplyState> ID_2_STATE = new HashMap<>();
   
static {
       
for(ApplyState state : values()) {
           
ID_2_STATE.put(state.id, state);
        }
    }

   
private String id;

    ApplyState(String id) {
       
this.id = id;
    }

   
@Override
   
public String id() {
       
return id;
    }

   
@Override
   
public ApplyState create(String s, Object o) {
       
return idOf(s);
    }

   
@Override
   
public ApplyState idOf(String s) {
       
return ID_2_STATE.get(s);
    }

   
@Override
   
public ApplyState state() {
       
return this;
    }

   
@Override
   
public Object extra() {
       
return null;
    }
}


 

ApplyAction


public enum ApplyAction implements Action<ApplyAction> {

   
ACCEPT("accept"), RETURN("return"), REJECT("reject"), DUMMY("dummy");
   
private static Map<String, ApplyAction> ID_2_ACTION = new HashMap<>();
   
static {
       
for(ApplyAction state : values()) {
           
ID_2_ACTION.put(state.id, state);
        }
    }

   
private String id;

    ApplyAction(String id) {
       
this.id = id;
    }

   
@Override
    
public String id() {
       
return id;
    }

   
@Override
    
public ApplyAction create(String s, Object o) {
        
return idOf(s);
    }

   
@Override
    
public ApplyAction idOf(String s) {
       
return ID_2_ACTION.get(s);
    }

   
@Override
    
public ApplyAction action() {
       
return this;
    }

   
@Override
    
public Object extra() {
       
return null;
    }
}


 


Role

public enum Role {

   
PIC, RM, RGM;

}



0 0