一:如果是单角色形式的状态改变(对于多个角色对象来说,他们看到的流程或者说是状态类型是一致的),此时可以使用状态机的模式进行改动,如果流程不是太复杂应该是可以做的
二:如果是多角色形式的状态改变(每个角色在同一阶段的状态都不相同),这时是一个复杂的流程关系,这时的解决思路是:首先把角色理清楚,每个阶段每个角色对应的状态,以及可能进行的操作,并且操作之后的状态改变,等等都要详细对应好,依次为每个角色创建属于自身的所有状态的一个状态类。
/*******************************************工单流程状态*******************************************/ /** * 动作 学员 客服 工厂 * 1 .学员提交 待受理 未受理 无 * 2 .客服提交 已受理 已受理(结束案件执行动作) 待处理 * 3 .工厂回复 处理中 待确认 已回复 * 4 .客服确认 处理中 工厂回复通过,学员可看回复 待回复(整改中) * 5 .学员确认 已解决(未解决重复2) 已解决 已解决(待员工确认) * 6 .学员关闭 已关闭 学员已关闭 已关闭 * 7 .客服关闭 已关闭 已关闭 已关闭 * 8 .递交微软处理 递交微软处理 递交微软处理 递交微软处理 * */ public static final long STUDENT_JOB_STATUS_PENDING = 1L; //待受理 public static final long STUDENT_JOB_STATUS_INPROCESS = 2L;//处理中 仅仅是待确认之前的状态 public static final long STUDENT_JOB_STATUS_CONFIRMED = 3L;//待确认 public static final long STUDENT_JOB_STATUS_COMPLETED = 4L;//已完成 public static final long STUDENT_JOB_STATUS_REVOKED = 5L;//已撤销 public static final long STUDENT_JOB_STATUS_NOT_SATISTIFACTION = 6L;//不满意 public static final long STUDENT_JOB_STATUS_RESCINDED = 7L;//员工主动关闭 public static final long STUDENT_JOB_STATUS_SEND_TO_MICROSOFT = 9L;//发送给微软 //操作员工单流程状态 public static final long OPERATOR_JOB_STATUS_PENDING = 1L; //未受理 public static final long OPERATOR_JOB_STATUS_INPROCESS = 2L; //已受理(等待工厂回复) public static final long OPERATOR_JOB_STATUS_CONFIRMED = 3L; //工厂已回复 public static final long OPERAOTR_JOB_STATUS_REJECTED = 4L; //已拒绝工厂回复 public static final long OPERATOR_JOB_STATUS_RESCINDED = 5L; //操作员主动关闭 public static final long OPERATOR_JOB_STATUS_COMPLETED = 6L; //已完成 public static final long OPERATOR_JOB_STATUS_WORKER_NOT_PASS = 7L;//员工不满意 public static final long OPERATOR_JOB_STATUS_WORKER_CONFIRM = 8L;//员工确认中 public static final long OPERATOR_JOB_STATUS_SEND_TO_MICROSOFT = 9L;//发送给微软 //工厂工单流程状态 public static final long FACTORY_JOB_STATUS_PENDING = 1L; //待处理 public static final long FACTORY_JOB_STATUS_INPROCESS = 2L; //处理中 public static final long FACTORY_JOB_STATUS_CONFIRMED = 3L; //待确认 public static final long FACTORY_JOB_STATUS_RESCINDED = 4L; //被操作员驳回工单 public static final long FACTORY_JOB_STATUS_COMPLETED = 5L; //已结束 public static final long FACTORY_JOB_STATUS_TOMICROSOFT = 6L; //客服工厂处理不了移交微软 public static final long FACTORY_JOB_STATUS_REJECTED = 7L; //员工不同意 public static final long FACTORY_JOB_STATUS_REVOKE = 8L; //撤销 add wz public static final long FACTORY_JOB_STATUS_CLOSE = 9L; //关闭 add wz //微软工单流程状态 public static final long MICRO_JOB_STATUS_NOT_NEED_HELP = 3L; // 无需协助 public static final long MICRO_JOB_STATUS_NEED_HELP = 4L; // 需要协助 public static final long MICRO_JOB_STATUS_COMPLETED = 5L; // 已结束 public static final long MICRO_JOB_STATUS_CLOSE = 7L; // 已关闭
三:步骤详解:
1.创建状态机接口
/** * 工单状态机 */ public interface CaseStatusMachineFactory { public CaseStatus getNextStatus(CaseStatus caseStatus, CaseEvent caseEvent); }
在这里是返回某一个状态,也可以返回每个角色所对应的状态(集合),可以单个判断,也可以先判断出一个在推导出其他几个对应的角色,都可以
2.总状态类
/** * 工单所有状态 * 思路:假如按照每个角色进行对应的角色转换,然后执行该步骤必要的操作,那么员工提交工单, * 此时不需要进行操作,保存工单信息之后就转为接线员待受理。此时接线员前台工单列表 * 是根据数字状态来显示对应的工单状态,那么此时可以进入下一个操作,接线员提交工单。 * 也就是说当前操作的判断还是根据数据库里的字段状态判断,和CASEStatus是没有关系的。 * 也就是说可以分离开来。互不干扰。那么能不能融合成一个?????????????? */ public enum CaseStatus { //员工 WORKER_SUBMIT("workerSubmit","员工提交工单"), CASE_REVOKED("caseRevoked","工单已撤销"), //接线员 OPE_WAIT_ACCEPT("opeWaitAccept","接线员待受理"), //OPE_WAIT_HANDLE("opeWaitHandle","接线员待处理"), OPE_AGREE_FAC_PLAN("opeAgreeFacPlan","接线员同意工厂解决方案"), OPE_DISAGREE_FAC_PLAN("opeDisAgreeFacPlan","接线员不同意工厂解决方案"), OPE_AGREE_FAC_SOLUTE("opeAgreeFacSolute","接线员同意工厂解决结果"), OPE_DISAGREE_FAC_SOLUTE("opeDisAgreeFacSolute","接线员不同意工厂解决结果"), OPE_AGREE_FAC_NO_QUES("opeAgreeFacNoQues","接线员同意工厂无此问题"), OPE_DISAGREE_FAC_NO_QUES("opeDidAgreeFacNoQues","接线员不同意工厂无此问题"), WORK_AGREE_FAC_REPLY("workAgreeFacReply","员工同意工厂解决情况"), WORK_DISAGREE_FAC_REPLY("workDisAgreeFacReply","员工不同意工厂解决情况"), //工厂(待工厂回复和工厂处理中是一样的) WAIT_FAC_REPLY("waitFacReply","等待工厂回复"), FAC_REPLY_PLAN("facReplyPlan","工厂回复解决方案"), FAC_REPLY_SOLUTE("facReplySolute","工厂回复解决结果"), FAC_REPLY_NO_QUES("facReplyNoQues","工厂无此问题"), OPE_CASE_CLOSE("opeCloseCase","接线员关闭工单"), MIC_CASE_CLOSE("micCloseCase","微软关闭工单"), //微软判断是否为危险工单 WAIT_MIC_PERMIT("waitMicPermit","等待微软审核"), ; private String status; private String desc; CaseStatus(String status, String desc) { this.status = status; this.desc = desc; } public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } }
理解:把该系统的所有角色经过某一个动作后变化的状态都列出来,越详细越好,因为每一个不同的状态所对应的操作都是不同的
3.响应事件:
/** * 工单所有响应事件 */ public enum CaseEvent { //员工 WORKER_SUBMIT("workerSubmit","员工提交工单"), WORKER_REVOKE("workerRevoke","员工撤销工单"), //接线员 OPE_ACCEPT_CASE("opeAcceptCase","受理工单"), OPE_SEND_MIC("opeSendMic","发送给微软"), OPE_SEND_FAC("opeSendFac","发送给工厂"), //OPE_AGREE_FAC_PLAN("opeAgreeFacPlan","接线员同意工厂解决方案"), //OPE_AGREE_FAC_SOLUTE("opeAgreeFacSolute","接线员同意工厂解决结果"), //工厂 FAC_REPLY_PLAN("facReplyPlan","工厂回复解决方案"), FAC_REPLY_SOLUTE("facReplySolute","工厂回复解决结果"), FAC_REPLY_NO_QUES("facReplyNoQues","工厂无此问题"), //微软 MIC_CONSIDER_DANGER("micConsiderDanger","微软认为危险"), MIC_CONSIDER_SAFE("micConsiderSafe","微软认为安全"), AGREE("agree","同意"), DISAGREE("disagree","不同意"), CASE_CLOSE("close","关闭"),; private String type; private String desc; CaseEvent(String type, String desc) { this.type = type; this.desc = desc; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } }
理解:该类里面是某个角色所有的动作,如果想要清晰点,最好每个角色一个类,根据上面分析的进行编写。每个角色当前是什么状态(通过数据库数字标识判断出来),然后进过某一个操作后,变成什么状态,在这个过程中,对该事件的细节进行操作,并保存数据库
4.状态机具体操作过程:
/** * 接线员工单状态机判断 * 注:普通的状态机都是对于各个角色是统一的状态,但是也不能用一个公共的返回状态,因为要进行某一 * 个操作的时候,必须要先判断该操作是否没做,然后才显示相应的按钮。而这个判断也就是通过数据库里的 * 某一个字段进行判断,因此可以说工作流就是通过状态机加上一系列的状态数字记录结合的一种技术 * 可以通过获取该状态来判断下一步操作。而多个角色有不同的状态时,此时状态的转换与获取是一个问题: * 解决方案:每个角色单独自己划分状态,也就是说每个状态机只负责管理本身角色的状态转换 */ public class CaseStatusMechine implements CaseStatusMachineFactory{ private Job job; public CaseStatusMechine(Job job){ this.job = job; } /***------------------------ 关键问题在于当前的状态如何判断,难不成每次进行操作前,都要根据数据库字段值来判断当前处在哪一个状态里面????同时返回的状态有什么用-----------------------------***/ /** * 一个通过工单来进行接线员本身所有状态判断的方法 */ @Autowired JobLogicInterface jobLogicInterface; /** * 接线员所有当前状态的判断,根据job的字段进行判断 * @param job * @return */ public CaseStatus getOpeState(Job job){ if(job.getJobOperatorStatus().equals(Job.OPERATOR_JOB_STATUS_PENDING)){ return CaseStatus.OPE_WAIT_ACCEPT; } return CaseStatus.OPE_WAIT_ACCEPT; } /** * 具体执行判断事件,该处应该执行相应的代码操作,如保存记录等 * OPE_WAIT_ACCEPT : 待受理过程中 * 注:返回下一个状态的值此时看起来毫无作用。 * @return */ public CaseStatus getNextStatus(CaseStatus caseStatus, CaseEvent caseEvent){ //进行提交 switch (caseStatus){ case WORKER_SUBMIT: return getStudentSubmitStatus(); //待受理 case OPE_WAIT_ACCEPT: return getOpeSubmitStatus(caseEvent); //接线员审核工厂解决方案 case FAC_REPLY_PLAN: return getOpePermitFacPlanStatus(caseEvent); //接线员审核工厂解决结果 case FAC_REPLY_SOLUTE: return getOpePermitFacSoluteStatus(caseEvent); //接线员审核工厂无此问题 case FAC_REPLY_NO_QUES: return getOpePermitFacNoQuesStatus(caseEvent); case WORK_AGREE_FAC_REPLY: return null; case WORK_DISAGREE_FAC_REPLY: return null; default: throw new RuntimeException("流程有问题啊"); } } /** * 获取接线员提交给微软还是工厂的状态 */ private CaseStatus getOpeSubmitStatus(CaseEvent event){ switch (event){ //发送给微软审核 case OPE_SEND_MIC: return CaseStatus.WAIT_MIC_PERMIT; //发送工厂进行处理 case OPE_SEND_FAC: return CaseStatus.WAIT_FAC_REPLY; default: throw new RuntimeException("暂不支持该类型的操作方式"); } } /** * 接线员处理工厂回复解决方案 * @param event */ private CaseStatus getOpePermitFacPlanStatus(CaseEvent event){ switch (event){ //接线员同意工厂解决方案 case AGREE: return CaseStatus.OPE_AGREE_FAC_PLAN; //接线员不同意工厂解决方案 case DISAGREE: return CaseStatus.OPE_DISAGREE_FAC_PLAN; default: throw new RuntimeException("暂不支持该操作"); } } /** * 接线员处理工厂回复解决结果 * @param event */ private CaseStatus getOpePermitFacSoluteStatus(CaseEvent event){ switch (event){ //接线员同意工厂解决结果 case AGREE: return CaseStatus.OPE_AGREE_FAC_SOLUTE; //接线员不同意工厂解决结果 case DISAGREE: return CaseStatus.OPE_DISAGREE_FAC_SOLUTE; default: throw new RuntimeException("暂不支持该操作"); } } /** * 接线员处理工厂回复无此问题 * @param event */ private CaseStatus getOpePermitFacNoQuesStatus(CaseEvent event){ switch (event){ //接线员同意工厂无此问题 case AGREE: return CaseStatus.OPE_AGREE_FAC_NO_QUES; //接线员不同意工厂无此问题 case DISAGREE: return CaseStatus.OPE_DISAGREE_FAC_NO_QUES; default: throw new RuntimeException("暂不支持该操作"); } }
}
理解:该状态机里面把某一角色(也可以多个角色)的所有事件响应都一一枚举,每个对应的事件操作以及结果也都展现出来。
总结:枚举所有状态对于我们编程可以变得有逻辑,每个角色只要专注自身的状态改变以及事件响应就行了,当前状态就通过数据库标识来判断(这个没有其他办法,因为这种不是单一空间程序(整个动作连续做完,这使得可以接受或者保存下一个状态,利用)),对于我们这种多空间层次(时间跨度大,动作可能很久才进行,并且伴随大并发)来说,只能进入一个就根据状态来判断一次当前状态,这种状态模式有一点好就是分工细腻,每个角色维护自身的状态即可,不用管别人的,但是程序堆在一起显得臃肿,另外的好处就是逻辑稍微清晰些。但是这样还是有待改进,期待后续方案。
案例:
/** * 学员提交具体测试 * @return */ private CaseStatus getStudentSubmitStatus(){ HotLineUtils.stuSaveJob(job); return CaseStatus.OPE_WAIT_ACCEPT; }
下面action方法里的所有的该操作以及状态改变都应该在此处修改,并且返回单个状态或者多个状态集合,看需求
action:
/**
* 员工提交工单
* @return
*/
public String stuSaveJobState(){
Student student = (Student) session.get("trainee");
//员工提交工单
Job job = new Job();
Date date = new Date();
job.setStudentId(student.getStudentId());
job.setJobNum("TT"+ RandomStringUtils.randomAlphanumeric(6).toLowerCase() + String.valueOf(new Date().getTime()));
job.setContent(obj.getContent());
job.setStudentName(student.getName());
if(obj.getStudentPhone() !=null && !obj.getStudentPhone().trim().equals("")){
job.setStudentPhone(obj.getStudentPhone());
}
if(obj.getStudentQq() !=null && !obj.getStudentQq().trim().equals("")){
job.setStudentQq(obj.getStudentQq());
}
if(obj.getStudentEmail() !=null && !obj.getStudentEmail().trim().equals("")){
job.setStudentEmail(obj.getStudentEmail());
}
job.setIfAnonymous(obj.getIfAnonymous());
job.setStudentCreateTime(date);
job.setCreateTime(date);
job.setChannel(Job.JOB_PUBLIST_CHANNEL_PLATFORM);
job.setContentText(StringUtils.HtmlToPlainText(obj.getContent()));
Organ organ = organizationLogicInterface.get(student.getOrgId());
job.setFactoryName(organ.getName());
CaseStatusMechine mechine = new CaseStatusMechine(job);
//当前员工提交状态,事件无(只有提交才需要做改变)
CaseStatus caseStatus = mechine.getNextStatus(CaseStatus.WORKER_SUBMIT,null);
if(caseStatus != null){
jsonMap.put("success",true);
return "stuSaveJobState";
}
jsonMap.put("success",false);
return "stuSaveJobState";
}