购物流程接口设计
为什么要设计这个接口
- 要实现代码校验和逻辑抽离
- 数据出错修复,打个比方说,用户支付订单后,但是订单数据还是未支付。
- 异步执行修复数据或者任务,不影响业务功能,加速请求校验。
- 打个比方说:服务商取消订单,需要发送消息给对应的用户终端,这个时候可以将发送功能接触数据修复接口,如果发送成功就不发送,发送失败就重试发送。
- 后期功能扩展容易
- 后期功能扩展可以在业务模块那边新增扩展接口,也可以action总模板路口那里做扩展设计
概念设计
总体设计
接下图
模板细节设计
策略细节设计
接下图
数据修复设计
shoppingFlowManager设计
handler()
- 对外暴露的业务执行接口,比如订单新增业务。
- 负责少部分的数据校验和所有的业务代码。
- 具体业务由子类具体实现。比如说orderAddOperation的handler方法
beforeHandler()
- 执行handler方法前的操作,控制handler和afterHandler方法的执行。
- 具体业务由子类具体实现。比如说orderAddOperation的beforeHandler方法
afterHandler()
- 执行handler方法之后的操作。
- 举例(异步执行):
- 订单支付成功了,但是由于cpu负载的原因,导致没有更新订单状态,这个时候需要去修复数据。
- 举例(异步执行):
- 具体业务由子类具体实现。比如说orderAddOperation的afterHandler方法。
check()
- 处理绝大部分数据校验,异常直接抛出给外部处理。
- 具体业务由子类具体实现。比如说orderAddOperation的check方法
saveLog()
- 记录日志接口,是对所有业务共性的抽象
action()
- 模板方法,该方法记录模板的整个流程。
- 后期共性功能的抽象可以这里动态扩展。
- 打个比方说:
- 假设后期所有业务都要做大数据分析,那么这里可以记录大数据分析所需要的基础数据。
- 打个比方说:
businessManager设计
- 模板具体实现,用来处理不同的业务。
- 对不同的业务功能可以做扩展处理。
businessOperation设计
- 该接口是核心的业务接口。
- 后期功能扩展只要实现该接口就能扩展功能加入到购物流程接口设计的体系中,比如说有个物流管理的功能,那么只要实现该接口,就如加入这套体系中。
- 是所有业务操作共性接口
- 方便做AOP扩展。比如说执行动作前,新增一些其他动作
- 案例:
- 订单新增操作
- 订单迁移操作
- 订单取消操作
ruleCheck设计
- 规则校验接口
- 是所有校验规则的父类。
- 方便做AOP扩展。比如说执行动作前,新增一些其他动作
- 案例:
- 订单新增规则
- 订单取消规则
other设计
- 后期扩展接口,是为了这套体系,不同业务操作那里功能做灵活扩展。
- 案例:订单新增操作,还需要自动生成对账单记录,其他业务不需要生成对账单
- 只要订单新增操作实现这个扩展接口,其他业务不需要实现该接口。
- 案例:订单新增操作,还需要自动生成对账单记录,其他业务不需要生成对账单
dataRepair设计
- 对业务数据的补偿性修复。
- 如果有的业务操作需要在afterHandler方法中,做数据修复,可以实现该接口
- 案例
- 订单新增:支付状态修复,如果订单微信支付成功了,但是状态还是未支付,那么实现该接口,并将修复线程放到修复任务队列中去就行,修复线程会被异步执行。
##数据修复设计
- 订单新增:支付状态修复,如果订单微信支付成功了,但是状态还是未支付,那么实现该接口,并将修复线程放到修复任务队列中去就行,修复线程会被异步执行。
- 案例
- 漏桶算法限制修复任务的执行。(限流)
- 任务执行的过程,队列中任务出列,线程池中线程执行该任务,不管失败还是成功,任务都从队列中丢弃。如果任务失败,持久化请求参数和对应的修复操作到数据库中。
日志核心参数的设计
- 业务类型
- 业务操作类型
- 业务请求参数
- 业务执行结果
- 操作人
- 操作日期
- 主键id
订单数据修复设计
-
支付前,执行修复动作,将数据保存到数据库中
-
修复动作:
- 间隔5秒(业务设置)后去修复数据
- 重试N次(业务设置)还失败就不去修复
- 手动页面修复。
-
线程主动开启或者关闭
使用规则
- 新增业务需要继承shoppingFlowManager抽象类
- 新增的业务操作需要实现businessOperation接口
- 如果有些业务操作需要数据修复,实现dataRepair接口
- 新增的业务操作规则实现ruleCheck接口
- 如果某些特殊的业务,需要做业务功能扩展,那么新增接口,businessManager包含新增接口。在businessManager实现的模板方法中,新增功能扩展。
购物流程接口设计案例
结构
接口代码
package com.xxx.businessManager.business;
/**
* @Author feizhou
* @Description 主动抛出异常处理
* @Date 11:18 2019/8/5
* @Param
* @return
**/
public class BusinessException extends RuntimeException{
private Integer code;
private ResultModel exMessage;//扩展需要返回的信息,如果该信息存在就返回该信息
public BusinessException(Integer code, String msg){
super(msg);
this.code=code;
}
public BusinessException(ResultModel exMessage,Integer code, String msg){
super(msg);
this.code=code;
this.exMessage=exMessage;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public ResultModel getExMessage() {
return exMessage;
}
public void setExMessage(ResultModel exMessage) {
this.exMessage = exMessage;
}
}
package com.xxx.businessManager.business;
/**
* @Author feizhou
* @Description 业务操作接口
* @Date 14:54 2019/8/2
* @Param
* @return
**/
public interface BusinessOperation {
/**
* @return
* @Author feizhou
* @Description 业务核心代码
* @Date 14:35 2019/8/2
* @Param []
**/
BusinessResult handler();
/**
* @return
* @Author feizhou
* @Description 业务核心代码后动作
* @Date 14:39 2019/8/2
* @Param []
**/
BusinessResult afterHandler();
/**
* @return void
* @Author feizhou
* @Description 业务核心代码前动作
* @Date 14:39 2019/8/2
* @Param []
**/
BusinessResult beforeHandler();
/**
* @return void
* @Author feizhou
* @Description 数据校验,直接抛异常
* @Date 14:38 2019/8/2
* @Param []
**/
void check();
}
package com.xxx.businessManager.business;
import java.util.Map;
/**
* @ClassName: ShoppingFlowManager
* @Description 业务执行结果
* @Author feizhou
* @Date 2019/8/2 14:33
* @Verson 1.0
**/
public class BusinessResult {
private boolean isRunHandler=true ; //是否执行Handler方法
private boolean isRunAfterHandler=true ;//是否执行AfterHandler方法
private Map<String, Object> operationParam;//业务参数
private String businessOperationType; //业务操作类型
private ResultModel returnObject;//返回给客户端的对象
public boolean isRunHandler() {
return isRunHandler;
}
public void setRunHandler(boolean runHandler) {
isRunHandler = runHandler;
}
public boolean isRunAfterHandler() {
return isRunAfterHandler;
}
public void setRunAfterHandler(boolean runAfterHandler) {
isRunAfterHandler = runAfterHandler;
}
public Map<String, Object> getOperationParam() {
return operationParam;
}
public void setOperationParam(Map<String, Object> operationParam) {
this.operationParam = operationParam;
}
public String getBusinessOperationType() {
return businessOperationType;
}
public void setBusinessOperationType(String businessOperationType) {
this.businessOperationType = businessOperationType;
}
public ResultModel getReturnObject() {
return returnObject;
}
public void setReturnObject(ResultModel returnObject) {
this.returnObject = returnObject;
}
}
package com.xxx.businessManager.business;
public interface DataRepair {
/**
* @Author feizhou
* @Description 数据修复
* @Date 15:50 2019/8/2
* @Param []
* @return com.lolaage.businessManager.BusinessResult
**/
void toRepair();
}
package com.xxx.businessManager.business;
import java.io.Serializable;
/**
* @Author feizhou
* @Description 返回的数据
* @Date 15:10 2019/8/8
* @Param
* @return
**/
public class ResultModel implements Serializable {
private static final long serialVersionUID = 1L;
private Integer errCode;
private String errMsg;
private Object returnData;//第一优先级,如果存在,就返回该数据,如果不存在,就需要分析errCode,errMsg
public ResultModel(Integer errCode, String errMsg) {
this.errCode = errCode;
this.errMsg = errMsg;
}
public ResultModel() {
}
public ResultModel(Integer errCode, String errMsg, Object returnData) {
this.errCode = errCode;
this.errMsg = errMsg;
this.returnData = returnData;
}
public Integer getErrCode() {
return errCode;
}
public void setErrCode(Integer errCode) {
this.errCode = errCode;
}
public String getErrMsg() {
return errMsg;
}
public void setErrMsg(String errMsg) {
this.errMsg = errMsg;
}
public Object getReturnData() {
return returnData;
}
public void setReturnData(Object returnData) {
this.returnData = returnData;
}
}
package com.xxx.businessManager.business;
/**
* @Author feizhou
* @Description 规则校验接口,直接抛异常
* @Date 15:31 2019/8/2
* @Param
* @return
**/
public interface RuleCheck {
/**
* @Author feizhou
* @Description 规则校验接口,直接抛异常
* @Date 15:32 2019/8/2
* @Param []
* @return void
**/
void check();
}
package com.xxx.businessManager.business;
/**
* @ClassName: ShoppingFlowManager
* @Description 业务流程模板
* @Author feizhou
* @Date 2019/8/2 14:33
* @Verson 1.0
**/
public abstract class ShoppingFlowManager {
private BusinessResult businessResult = new BusinessResult();
/**
* @return void
* @Author feizhou
* @Description 方法模板
* @Date 15:46 2019/8/2
* @Param []
**/
public final BusinessResult action() {
try {
saveLog();
check();
businessResult = beforeHandler();
if (businessResult.isRunHandler()) {
businessResult = handler();
}
if (businessResult.isRunAfterHandler()) {
businessResult = afterHandler();
}
} catch (Exception e) {
ResultModel m=new ResultModel();
//打印异常
e.printStackTrace();
if (e instanceof BusinessException) {
//检测主动抛出的异常
BusinessException be = (BusinessException) e;
if(be.getExMessage()!=null){
m=be.getExMessage();
}else {
m.setErrCode(be.getCode());
m.setErrMsg(be.getMessage());
}
} else {
//系统异常
m.setErrCode(-2);
m.setErrMsg(e.getMessage());
}
businessResult.setReturnObject(m);
}
return businessResult;
}
/**
* @return java.util.Map
* @Author feizhou
* @Description 记录日志
* @Date 14:35 2019/8/2
* @Param []
**/
private void saveLog() {
}
/**
* @return
* @Author feizhou
* @Description 业务核心代码
* @Date 14:35 2019/8/2
* @Param []
**/
public abstract BusinessResult handler();
/**
* @return
* @Author feizhou
* @Description 业务核心代码后动作
* @Date 14:39 2019/8/2
* @Param []
**/
public abstract BusinessResult afterHandler();
/**
* @return void
* @Author feizhou
* @Description 业务核心代码前动作
* @Date 14:39 2019/8/2
* @Param []
**/
public abstract BusinessResult beforeHandler();
/**
* @return void
* @Author feizhou
* @Description 数据校验,直接抛异常
* @Date 14:38 2019/8/2
* @Param []
**/
public abstract void check();
}
package com.xxx.businessManager.workTask;
import com.xxx.businessManager.business.DataRepair;
public class Task {
/**
* @Author feizhou
* @Description 需要执行的方法
* @Date 9:57 2019/8/12
* @Param
* @return
**/
private DataRepair dataRepair;
/**
* @Author feizhou
* @Description 方法执行失败后,重试次数,默认3
* @Date 9:58 2019/8/12
* @Param
* @return
**/
private int tryTime;
/**
* @Author feizhou
* @Description 方法执行失败后,重试尝试的间隔时间,算法:t=(intervalBaseTime的2*alreadyhasRunTime幂函数)
* @Date 9:58 2019/8/12
* @Param
* @return
**/
private int intervalTime[]={3,6,9};
/**
* @Author feizhou
* @Description 方法执行失败后,已经执行的运行次数
* @Date 9:58 2019/8/12
* @Param
* @return
**/
private int alreadyhasRunTime;
public int getTryTime() {
return tryTime;
}
public int getIntervalTime() {
return intervalTime[alreadyhasRunTime]*1000;
}
//修复数据的接口
public DataRepair getDataRepair() {
return dataRepair;
}
public void setDataRepair(DataRepair dataRepair) {
this.dataRepair = dataRepair;
}
//构造方法
public Task(DataRepair dataRepair) {
this.dataRepair = dataRepair;
this.tryTime = intervalTime.length;
}
public Task(DataRepair dataRepair, int ... intervalTime) {
this.dataRepair = dataRepair;
this.tryTime = intervalTime.length;
this.intervalTime=intervalTime;
}
//已经运行的次数
public int getAlreadyhasRunTime() {
return alreadyhasRunTime;
}
public void setAlreadyhasRunTime(int alreadyhasRunTime) {
this.alreadyhasRunTime = alreadyhasRunTime;
}
}
package com.xxx.businessManager.workTask;
import com.xxx.businessManager.business.BusinessException;
import com.xxx.businessManager.business.DataRepair;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
/**
* @Author feizhou
* @Description 处理一些非事务异步任务, 也就是对数据一致性要求不高的功能
* @Date 14:53 2019/7/22
* @Param
* @return
**/
public class TaskWork {
/**
* @Author feizhou
* @Description 线程池中默认线程的个数为1
* @Date 14:31 2019/7/22
**/
private static int worker_num = 1;
/**
* @Author feizhou
* @Description 任务队列
* @Date 14:31 2019/7/22
**/
private static BlockingQueue<Task> taskQueue = new LinkedBlockingDeque<>();
/**
* @Author feizhou
* @Description 工作线程
* @Date 14:31 2019/7/22
**/
private static WorkThread[] workThrads;
private static TaskWork taskWork=new TaskWork();
public static void dispatch(Task task) {
try {
taskQueue.put(task);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//构造私有,防止开发者实例化类Single,创建出新的single
private TaskWork() {
init();
};
public static TaskWork getTaskWork() {
return taskWork;
}
/**
* @Author feizhou
* @Description 构造方法执行完毕, 也就是类加载完成后执行intit方法
* @Date 14:31 2019/7/22
**/
public void init() {
workThrads = new WorkThread[worker_num];
for (int i = 0; i < worker_num; i++) {
workThrads[i] = new WorkThread();
workThrads[i].setName("LimitTaskThread-" + i);
workThrads[i].start();// 开启线程池中的线程
}
}
/**
* @Author feizhou
* @Description 定义工作线程
* @Date 14:52 2019/7/22
* @Param
* @return
**/
private class WorkThread extends Thread {
@Override
public void run() {
while (true) {
try {
//获取队列的任务
//阻塞式方法,没有元素就等待
Task task = taskQueue.take();
runTask(task);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* @Author feizhou
* @Description 执行任务
* @Date 10:11 2019/8/12
* @Param []
* @return void
**/
private void runTask(Task task) throws InterruptedException {
//方法执行错误后的执行次数
int tryTime = task.getTryTime();
//已经修复的次数
int alreadyhasRunTime = task.getAlreadyhasRunTime();
if(alreadyhasRunTime<tryTime){
//间隔时间
int intervalTime = task.getIntervalTime();
task.setAlreadyhasRunTime(++alreadyhasRunTime);
try {
System.out.println("第"+alreadyhasRunTime+"次修复数据执行--"+"间隔时间:"+intervalTime+"--请求的参数"+task.getDataRepair());
Thread.sleep(intervalTime);
task.getDataRepair().toRepair();
}catch (Exception e){
//阻塞式方法, 元素满就等待
if(alreadyhasRunTime<tryTime){
taskQueue.put(task);
}
}
}
}
}
public static void main(String[] args) {
DataRepair dataRepair= new DataRepair() {
@Override
public void toRepair() {
throw new BusinessException(2,"333");
}
};
Task task=new Task(dataRepair, 3,4);
TaskWork.dispatch(task);
}
}
使用案例(订单取消)
/**
* @Author feizhou
* @Description 活动订单管理:用户取消订单
* @Date 16:22 2019/8/2
* @Param
* @return
**/
@Transactional
public class OutingOrderForUserCancel implements BusinessOperation {
private static Logger log = Logger.getLogger(OutingOrderForUserCancel.class);
//请求参数
private QuitParam quitParam;
//全局变量,校验的时候会初始化
private Business_activity_order order;
private BusinessResult businessResult = new BusinessResult();
//服务层
private IBusinessActivityOrderService businessActivityOrderService;
/**
* @return void
* @Author feizhou
* @Description 初始化服务层
* @Date 10:17 2019/8/6
* @Param []
**/
private void initService() {
businessActivityOrderService = (IBusinessActivityOrderService) ContextUtil.getBean("businessActivityOrderService");
};
//请求参数
public OutingOrderForUserCancel(QuitParam quitParam) {
this.quitParam=quitParam;
initService();
}
/**
* @return com.lolaage.businessManager.BusinessResult
* @Author feizhou
* @Description 处理业务
* @Date 16:23 2019/8/2
* @Param []
**/
@Override
public BusinessResult handler() {
JsonModel result = toCancel();
ResultModel resultModel = new ResultModel(0, "success");
resultModel.setReturnData(result);
businessResult.setReturnObject(resultModel);
return businessResult;
}
/**
* @Author feizhou
* @Description 取消訂單,已经将校验逻辑抽离的业务逻辑
* @Date 10:22 2019/8/19
* @Param
* @return void
**/
private JsonModel toCancel() {
return null;
}
/**
* @Author feizhou
* @Description 异步tcp发通知
* @Date 10:48 2019/8/20
* @Param
* @return void
**/
private void sendMsgForTCP(Long userId, String authCode, String socketSvrIp, Long outingId, Long startTime, Byte type, Long uid, Long memberOrderId) {
DataRepair dataRepair=new OutingOrderForUserCancelSendTCPMsgDataRepair( userId, authCode, socketSvrIp, outingId, startTime, type, uid, memberOrderId );
HashMap<String, Object> pa = new HashMap<>();
pa.put("dataRepair",dataRepair);
Task task=new Task(dataRepair, 1,60,180);
TaskWork.dispatch(task);
}
@Override
public BusinessResult afterHandler() {
//订单取消完成,需要通知终端,但是调用终端是其他的服务,
// 且和当前的功能事务无关,那么我们将它定义为需要修复的数据,让它异步去执行
//这里我为了简化代码,就传null值
sendMsgForTCP( null, null, null, null, null, null, null, null );
return businessResult;
}
@Override
public BusinessResult beforeHandler() {
//做一些数据的修补工作,比如说本来IOS端需要传递一个参数status过来的,但是现在对应的版本没有传
//但是现在IOS版本已经上线了,来不及修改,那么我们可以这里做下判断补充
return businessResult;
}
@Override
public void check() {
//处理数据校验和初始全局变量
order = businessActivityOrderService.findById(quitParam.getOrderId());
//订单校验
orderCheck(order);
}
/**
* @Author feizhou
* @Description 订单校验
* @Date 11:18 2019/8/15
* @Param [order]
* @return void
**/
private void orderCheck(Business_activity_order order) {
if(order == null){
throw new BusinessException(SystemErrCode.REQ_PARAM_ERROR,"订单不存在");
}
//'订单状态:0-待支付,1-已支付,2-订单异常,6-已取消 ,7部分退出,8已完成',
if(order.getStatus()==6){
throw new BusinessException(SystemErrCode.REQ_PARAM_ERROR,"订单已取消");
}
}
}
/**
* @Author feizhou
* @Description 活动订单管理:用户取消订单,发送tcp数据修复
* @Date 16:22 2019/8/2
* @Param
* @return
**/
@Transactional
public class OutingOrderForUserCancelSendTCPMsgDataRepair implements DataRepair {
private static Logger log = Logger.getLogger(OutingOrderForUserCancelSendTCPMsgDataRepair.class);
//请求参数
private Long userId;
private String authCode;
private String socketSvrIp;
private Long outingId;
private Long startTime;
private byte type;
private Long uid;
private Long memberOrderId;
//请求参数
public OutingOrderForUserCancelSendTCPMsgDataRepair(Long userId, String authCode, String socketSvrIp, Long outingId, Long startTime, byte type, Long uid, Long memberOrderId) {
this.userId = userId;
this.authCode = authCode;
this.socketSvrIp = socketSvrIp;
this.outingId = outingId;
this.startTime = startTime;
this.type = type;
this.uid = uid;
this.memberOrderId = memberOrderId;
}
@Override
public void toRepair() {
new OutingAction().commOutingMemberEntry( userId, authCode, socketSvrIp, outingId, startTime, type, uid, memberOrderId);
}
@Override
public String toString() {
return "OutingOrderForCancelSendTCPMsgDataRepair{" +
"userId=" + userId +
", authCode='" + authCode + '\'' +
", socketSvrIp='" + socketSvrIp + '\'' +
", outingId=" + outingId +
", startTime=" + startTime +
", type=" + type +
", uid=" + uid +
", memberOrderId=" + memberOrderId +
'}';
}
}
好处
- 校验和逻辑抽离
- 数据出错修复
- 异步执行修复数据或者任务,不影响业务功能,加速请求校验
- 后期接口扩展容易